博客系统前端实现

Posted YoLo♪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了博客系统前端实现相关的知识,希望对你有一定的参考价值。

目录

1.预期效果

2.实现博客列表页

3.实现博客正文页

4.实现博客登录页

5.实现博客编辑页面


1.预期效果

对前端html,css,js有大致的了解后,现在我们实现了一个博客系统的前端页面.一共分为四个页面没分别是:登陆页面,博客列表页,博客正文页,博客编辑页

我们看下四个界面

登陆页面

博客列表页 

博客正文页 

博客编辑页 

博客系统前端目录 

 

2.实现博客列表页

我们可以发现上述四个页面的导航栏,背景的样式都是相同的,所以这部分可以单独实现一个css代码,使用时直接引入即可

当实现每个页面不同效果时,再实现单独的css代码

博客列表页的代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客列表页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog_list.css">
</head>
<body>
    <!--1. 这是导航栏 -->
    <!-- 先实现导航栏,每个页面都得有导航栏.把导航栏样式单独放到一个common.css中,让各个页面来引用 -->
    <div class = "nav">
        <img src="image/title.png" alt="">
        <span class = "title">我的博客系统</span>
       <!-- 这个标签用来占位,将另外三个挤到导航页面右侧 -->
        <span class="spacer"></span>
        <a href="#">主页</a>
        <a href="#">写博客</a>
        <a href="#">注销</a>
    </div>
    <!-- 页面主体部分 -->
    <div class="container">
        <!-- 左侧信息 -->
        <div class="container-left">
                <!-- 表示用户信息 -->
            <div class="card">
                <!-- 头像 -->
                <img src="image/touxiang.jpg" alt="">
                <!-- 用户名 -->
                <h3>YoLo</h3>
                <a href="#">gitee地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧信息 -->
        <div class="container-right">
            <!--表示一篇博客 -->
            <div class="blog">
                <!-- 博客标题 -->
                <div class="title">
                    第一篇blog
                </div>
                <!-- 事件 -->
                <div class="date">
                    2023-3-30
                </div>
                <!-- 摘要 -->
                <div class="desc">
                    1.接收请求的过程用户在浏览器输入一个URL(统一资源定位器),此时浏览器会构造一个HTTP请求这个请求会经过网络协议栈逐层封装成二进制Bit流,最终通过物理层设备转换成光信号/电信号传输出去这些传输出去的信号,会通过一系列网络设备到达目的主机服务器收到光信号/电信号.通过网络协议栈逐层分用,还原成HTTP请求.并交给Tomcat进程进行处理(通过端口号确定进程)
                </div>
                <!-- 查看全文 -->
                <a href="#">查看全文&gt&gt;</a>
            </div>

            <!--表示一篇博客 -->
            <div class="blog">
                <!-- 博客标题 -->
                <div class="title">
                    第二篇blog
                </div>
                <!-- 事件 -->
                <div class="date">
                    2023-3-30
                </div>
                <!-- 摘要 -->
                <div class="desc">
                    2.根据请求计算响应调用的方法(doGET/doPOST)中,就能执行到我们编写的代码.我们自己的代码可以根据请求中的一些信息给, 来给HttpServletResponse对象设置一些属性,例如状态码, header, body 等
                </div>
                <!-- 查看全文 -->
                <a href="#">查看全文&gt&gt;</a>
            </div>

            <!--表示yi篇博客 -->
            <div class="blog">
                <!-- 博客标题 -->
                <div class="title">
                    第三篇blog
                </div>
                <!-- 事件 -->
                <div class="date">
                    2023-3-30
                </div>
                <!-- 摘要 -->
                <div class="desc">
                    3.返回响应被调用的方法执行完毕后,Tomcat就会自动把HttpServletResponse这个我们刚设置好的对象转换成一个符合HTTP协议的字符串,通过Socket将响应发送出去此时响应数据在服务器主机上通过网络协议栈层层封装得到二进制bit流,通过物理层硬件设备转换成光信号/电信号传输出去
                </div>
                <!-- 查看全文 -->
                <a href="#">查看全文&gt&gt;</a>
            </div>
        </div>
    </div>
</body>
</html>

&gt代表>,直接输入>会被当成标签 

common.css

存放的是导航栏,背景的样式.每个页面都会用到,上述详情页直接引入该代码 

/* 写样式的起手式,先取出浏览器的公共样式,并且设置border-box,避免元素盒子被内边距和边框撑大 */
*
    margin: 0;
    padding: 0;
    box-sizing:border-box;

html,body
    /* html是页面最顶层元素,高度100% (相对父元素来说)和父元素是一样高的 
        对html标签来说,父元素就是浏览器窗口
        body父亲是html,设为100%.意思是body和html一样高,此时,body和html的高度都是和浏览器一样高
        不设置高度,默认设置取决于内部的内容多少
    */

    height: 100%;

body
    background-image: url(../image/background_image.png);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center center;

.nav
    /*  
    导航栏设置
    宽度:和父元素相同
    块级元素默认:width:100%
    */
    width: 100%;
    height: 50px;
    background-color: rgba(62, 56, 56, 0.7);
    color: aliceblue;
    /*导航栏里面的元素设置弹性布局*/
    display: flex;
    /* 垂直居中 */
    align-items: center;


.nav img
    width: 40px;
    height: 40px;
    /* 添加边距 */
    margin-left: 30px;
    margin-right: 10px;
    /*  图标变圆
        把内切圆半径设置成宽度的一半,就圆形了
    */
    border-radius: 50%;


.nav a
    color: aliceblue;
    /* 去掉下划线 */
    text-decoration: none;
    /* 让几个a标签不要贴在一起,加上内边距 
        此处使用外边距也行,内边距更好,内边距是元素的内容,可增大用户点击面积
    */
    padding: 0 10px;


.nav .spacer
    width: 70%;


/* 页面主体部分 */
.container
    /* 主体部分宽度 */
    width: 1000px;
    /* 主体部分高度.整个页面高度减去设置的导航栏高度 */
    height: calc(100% - 50px);
    /* 这里-两端必须有空格 */
    margin: 0 auto;
    /* 让我们先能看见 */
    /* background-color:#505050; */
    /* 设置弹性布局 */
    display: flex;
    /* 水平居中 */
    align-items: center;
    justify-content: space-between;

.container-left
    /* 100%相对于父元素,它的父元素是container,已设置过了 */
    height: 100%;
    width: 200px;
    /* background-color: #fff; */

.container-right
    /* 100%相对于父元素,它的父元素是container,已设置过了 */
    height: 100%;
    /* 留一个缝隙 */
    width: 795px;
    /* background-color: #df2222; */
    background-color: rgb(255, 255, 255,0.8);
    border-radius: 10px;

    /* 让元素带滚动条 */
    /* 如果内容溢出,加上滚动条,如果内容没有溢出,不加滚动条 */
    overflow: auto;


/* 左侧信息栏 */
.card
    background-color: rgb(255, 255, 255,0.8);
    border-radius: 10px;
    padding: 30px;


/* 用户头像  上下左右留30 长宽140px*/
.card img
    width: 140px;
    height: 140px;
    border-radius: 50%;


/* 用户名 */
.card h3
    /* 文字居中 */
    text-align: center;
    /* 让文字和上下都有边距  建议使用内边距 */
    padding: 10Px;


/* 码云地址 
    a标签是行内元素,设置居中要先设置成块级元素再居中
*/
.card a
    
    text-align: center;
    display: block;
    color: #505050;
    text-decoration: none;
    padding: 10px;


/*文章分类  */
.card .counter
/* 弹性布局 使得元素水平方向均匀排列 */
    display: flex;
    justify-content: space-around;
    padding: 5px;

   去掉下划线
   text-decoration: none;

让元素带滚动条,如果内容溢出,加上滚动条,如果内容没有溢出,不加滚动条

    overflow: auto;

blog_list.css

实现的是博客列表专用的样式(题目,日期,段落居中,段落缩进等) 

/* 博客列表页专用的实现样式 */


/* 设置容器元素样式 */

.blog
    width: 100%;
    padding: 20px;


/* 标题 */
.blog .title
    font-size: 24px;
    font-weight: 700;
    text-align: center;
    padding: 10px;


/* 日期 */
.blog .date
    text-align: center;
    color: #08bc6e;
    padding: 10px;


/*缩进 处理摘要 */
.blog .desc
    text-indent: 2em;


/* 查看全文 */
.blog a
    /* 转块级元素 */
    display: block;
    width: 120px;
    height: 40px;
    /* 水平居中 */
    margin-top: 20px ;
    margin-left: auto;
    margin-right: auto;
    /* 设置边框 */
    border:2px solid black;

    /* 文字水平居中 */
    text-align: center;
    /* 文字垂直居中 */
    line-height: 40px;
    
    /* 去掉下划线 */
    text-decoration: none;
    /* 文字黑色 */
    color: black;
    border-radius: 3px;

    /* 给鼠标悬停加上过渡效果 */
    transition: all 0.3s;


/*悬停变色 */
.blog a:hover
    color: white;
    background:#666;

像a标签这样的行内元素不好处理,先转成块级元素然后再处理 

缩进
.blog .desc
    text-indent: 2em;

给鼠标悬停加上过渡效果
    transition: all 0.3s;

悬停变色
.blog a:hover
    color: white;
    background:#666;

 3.实现博客正文页

blog_detail.html

博客正文,引入common.css,添加正文内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客详情页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog_detail.css">
</head>
<body>
    <!-- 添加导航栏 -->
    <!--1. 这是导航栏 -->
    <!-- 先实现导航栏,每个页面都得有导航栏.把导航栏样式单独放到一个common.css中,让各个页面来引用 -->
    <div class = "nav">
        <img src="image/title.png" alt="">
        <span class = "title">我的博客系统</span>
       <!-- 这个标签用来占位,将另外三个挤到导航页面右侧 -->
        <span class="spacer"></span>
        <a href="#">主页</a>
        <a href="#">写博客</a>
        <a href="#">注销</a>
    </div>
    <div class="container">
        <!-- 左侧信息 -->
        <div class="container-left">
                <!-- 表示用户信息 -->
            <div class="card">
                <!-- 头像 -->
                <img src="image/touxiang.jpg" alt="">
                <!-- 用户名 -->
                <h3>YoLo</h3>
                <a href="#">gitee地址</a>
                <div class="counter">
                    <span>文章</span>
                    <span>分类</span>
                </div>
                <div class="counter">
                    <span>2</span>
                    <span>1</span>
                </div>
            </div>
        </div>
        <!-- 右侧信息 -->
        <div class="container-right">
            <!-- 博客标题 -->
            <h3 class = "title">我的第一篇博客</h3>
            <!-- 发布时间 -->
            <div class="date">2023-3-30</div>
            <!-- 正文 -->
            <div class="content">
                <p>
                    1.接收请求的过程用户在浏览器输入一个URL(统一资源定位器),此时浏览器会构造一个HTTP请求这个请求会经过网络协议栈逐层封装成二进制Bit流,最终通过物理层设备转换成光信号/电信号传输出去这些传输出去的信号,会通过一系列网络设备到达目的主机服务器收到光信号/电信号.通过网络协议栈逐层分用,还原成HTTP请求.并交给Tomcat进程进行处理(通过端口号确定进程)
                </p>
                <p>
                    2.根据请求计算响应调用的方法(doGET/doPOST)中,就能执行到我们编写的代码.我们自己的代码可以根据请求中的一些信息给, 来给HttpServletResponse对象设置一些属性,例如状态码, header, body 等
                </p>
                <p>
                    3.返回响应被调用的方法执行完毕后,Tomcat就会自动把HttpServletResponse这个我们刚设置好的对象转换成一个符合HTTP协议的字符串,通过Socket将响应发送出去此时响应数据在服务器主机上通过网络协议栈层层封装得到二进制bit流,通过物理层硬件设备转换成光信号/电信号传输出去
                </p>
            </div>
        </div>
    </div>
</body>
</html>

blog_detail.css

博客详情页使用的样式

/* 这是博客详情页使用 */

/*  标题*/
.container-right .title
    text-align: center;
    padding: 10px;


/*日期  */
.container-right .date
    color: #1fbf76;
    text-align: center;
    padding: 10px;


/*缩进 处理摘要 */
.container-right .content p
    text-indent: 2em;
    padding: 10px 30px;


 4.实现博客登录页

login.html

引入commom.css添加对话框

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登陆页面</title>
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/login.css">
</head>
<body>
    <!-- 这个页面咩有注销按钮 -->
     <!--1. 这是导航栏 -->
    <!-- 先实现导航栏,每个页面都得有导航栏.把导航栏样式单独放到一个common.css中,让各个页面来引用 -->
    <div class = "nav">
        <img src="image/title.png" alt="">
        <span class = "title">我的博客系统</span>
       <!-- 这个标签用来占位,将另外三个挤到导航页面右侧 -->
        <span class="spacer"></span>
        <a href="#">主页</a>
        <a href="#">写博客</a>
        <!-- <a href="#">注销</a> -->
    </div>

    <!-- 正文部分 -->
    <!-- 贯穿整个页面的的容器 -->
    <div class="login-container">
        <!-- 垂直水平居中的对话框 -->
        <div class="login-dialog">
            <h3>登录</h3>
            <div class="row">
                <span>用户名</span>
                <!-- placeholder="手机号/邮箱"这个属性用来设置框内默认的文本 -->
                <input type="text" id="username" placeholder="手机号/邮箱">
            </div>
            <div class="row">
                <span>密码</span>
                <input type="password" id = "password">
            </div>
            <div class="row">
                <button id = "submit">登录</button>
            </div>
        </div>
       
    </div>
</body>
</html>

 <input type="text" id="username" placeholder="手机号/邮箱"> 

placeholder="手机号/邮箱"   

对话框中显示文本,点击后消失,没有输入内容又会出现

 

login.css

登录页面的样式

/* 这个文件是写登录页面样式 */

.login-container
    width: 100%;
    height: 100%;
    /* background-color: rgb(130, 0, 0); */
    /* 使对话框水平垂直居中 */
    display: flex;
    justify-content: center;
    align-items: center;


.login-dialog
    width: 380px;
    height: 330px;
    /* background-color: #fff; */
    /* display: flex; */
    background-color:rgba(255, 255, 255, 0.8);
    border-radius: 10px;


.login-dialog h3
    text-align: center;
    padding: 50px;

.login-dialog .row
    height: 50px;
    display: flex;
    justify-content: center;
    align-items: center;


.login-dialog .row span
    width: 100px;


#username, #password
    width: 200px;
    height: 40px;
    border-radius: 5px;
    border: none;

    /* 放大字体 */
    font-size: 17px;
    padding: 5px;


/* 提交按钮 */
#submit
    width: 300px;
    height: 40px;
    border-radius: 5px;
    border: none;
    color: aliceblue;
    background-color:#27c079;


#submit:active
    /* 点击变色 */
    background-color: darkgrey;

5.实现博客编辑页面

编辑器:markdown

引入 editor.md

editor.md 是一个开源的页面 markdown 编辑器组件

1)从官网上下载到压缩包. 放到目录中. 目录结构如下

2) 引入 editor.md

注意<script src="js/jquery.min.js"></script>要在下面三个之前引入

<!-- 引入 editor.md 的依赖 -->
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<script src="js/jquery.min.js"></script>

<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.js"></script>

 3) 初始化 editor.md

// 初始化编辑器
var editor = editormd("editor", 
    // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.
    width: "100%",
    // 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度
    height: "calc(100% - 50px)",
    // 编辑器中的初始内容
    markdown: "# 在这里写下一篇博客",
    // 指定 editor.md 依赖的插件路径
    path: "editor.md/lib/"
);

blog_edit.html

博客编辑页代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>博客编辑页</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/blog_edit.css">
    <script src="js/jquery.min.js"></script>
    <!-- 或者直接写网络路径 src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js" -->
    <!-- 引入md的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="editor.md/lib/marked.min.js"></script>
    <script src="editor.md/lib/prettify.min.js"></script>
    <script src="editor.md/editormd.js"></script>
</head>
<body>
    <!-- 这个页面咩有注销按钮 -->
     <!--1. 这是导航栏 -->
    <!-- 先实现导航栏,每个页面都得有导航栏.把导航栏样式单独放到一个common.css中,让各个页面来引用 -->
    <div class = "nav">
        <img src="image/title.png" alt="">
        <span class = "title">我的博客系统</span>
       <!-- 这个标签用来占位,将另外三个挤到导航页面右侧 -->
        <span class="spacer"></span>
        <a href="#">主页</a>
        <!-- <a href="#">写博客</a> -->
        <a href="#">注销</a>
    </div>

    <!-- 编辑区的容器 -->
    <div class="blog-edit-container">
        <div class="title">
            <input type="text" id="title" placeholder="请输入文章标题">
            <button id="submit">发布文章</button>
        </div>
    <!-- 博客编辑器,这里用id是为了和markdown编辑器对接而设置的 -->
        <div id="editor">

        </div>
    </div>
    <script>
     // 初始化编辑器
        var editor = editormd("editor", 
            // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.
            width: "100%",
            // 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度
            height: "calc(100% - 50px)",
            // 编辑器中的初始内容
            markdown: "# 在这里写下一篇博客",
            // 指定 editor.md 依赖的插件路径
            path: "editor.md/lib/"
        );
    </script>
</body>
</html>

blog_edit.css

博客编辑页专用样式

/* 这个页面用来写编辑文章 */

.blog-edit-container
    width: 1000px;
    height: 100%;
    /* background-color: #ffdc00; */
    margin: 0 auto;


.blog-edit-container .title
    height: 50px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    


#title
    height: 40px;
    width: 895px;
    border-radius: 6px;
    border: none;
    font-size: 20px;
    /* 去掉鼠标悬停后出现的轮廓线 */
    outline: none;
    /* 设置输入框半透明 */
    background-color: rgba(255,255,255,0.6);

#submit
    height: 40px;
    width: 100px;
    border-radius: 6px;
    border: none;
    color: black;
    background-color:orange;

/* 使用伪类设置鼠标点击后不再透明 */
/* 伪类选择器选择的是元素的状态,正常选择器选择元素 */
#title:focus
    background-color: rgba(255,255,255);

#submit:active
    /* 点击变色 */
    background-color: darkgrey;

#editor
    border-radius: 10px;
    opacity: 80%;

 

Python 实现个人博客系统

项目描述

开发环境:PyCharm、python3.7、MySQL5.5

使用技术:服务端是使用Flask开发的,前端是使用的Layui和Markdown编辑器所实现的。

项目包含功能如下:

  • 注册:注册账号

  • 登录:通过账号密码进行登录

    • 写博客:写博客采用的Markdown编辑器完成的。可以发布自己的博客

    • 我的博客:查看自己发布的博客并对其管理

    • 我的评论:查看自己的所有评论并对其管理

    • 修改密码

  • 查看博客列表:查看所有已发布的博客

  • 博客详情页:查看博客内容及评论信息,可以对当前博客进行评论

  • 关于

项目目录

数据库设计

数据库一共设计了三张表:用户表、博客表、评论表。
表之间的映射关系如下:

用户表和博客表一对多关系;用户和评论表一对多关系;博客表和评论表一对多关系。

其表的模型类代码如下:

class User(db.Model):
    # 设置表名
    __tablename__ = 'tb_user';
    # id,主键并自动递增
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(64), unique=True)
    password = db.Column(db.String(256), nullable=True)
    name = db.Column(db.String(64))

    # 设置只可写入,对密码进行加密
    def password_hash(self, password):
        self.password = generate_password_hash(password);

class Blog(db.Model):
    __tablename__ = 'blog'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(128))
    text = db.Column(db.TEXT)
    create_time = db.Column(db.String(64))
    #关联用户id
    user_id = db.Column(db.Integer, db.ForeignKey('tb_user.id'))
    user = db.relationship('User', backref='user')

class Comment(db.Model):
    __tablename__ = 'comment'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    text = db.Column(db.String(256))    # 评论内容
    create_time = db.Column(db.String(64))
    # 关联博客id
    blog_id = db.Column(db.Integer, db.ForeignKey("blog.id"))
    # 关联用户id
    user_id = db.Column(db.Integer, db.ForeignKey("tb_user.id"))
    blog = db.relationship("Blog", backref="blog")
    user = db.relationship("User", backref="use")

功能实现

页面基本模板实现

页面使用的是Jinja2模板,Jinja2支持页面继承,所以导航栏重复性的页面代码,我们都可以写在一个文件中。这里我们先创建一个base.html文件,编写页面大致的框架。其他模块直接继承使用即可。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %}
            {# 其他页面可以重写标题 #}
        {% endblock %}
    </title>
    <link rel="stylesheet" href="/static/layui/css/layui.css">
    <link rel="stylesheet" href="/static/css/base.css">
    <script src="/static/js/jquery.js"></script>
    <script src="/static/layui/layui.js"></script>
    {% block css %}
    {% endblock %}
</head>
<body>
<div id="bg"></div>
<ul class="layui-nav" lay-filter="">
    <li class="layui-nav-item"><a href="/">在线博客平台</a></li>
    {% if username %}
        <li class="layui-nav-item{% block updatepwd_class %}{% endblock %}"><a href="/updatePwd">修改密码</a></li>
    {% endif %}
    <li class="layui-nav-item{% block blog_class %}{% endblock %}"><a href="/blog/blogAll">博客</a></li>
    <li class="layui-nav-item{% block about_class %}{% endblock %}"><a href="/about">关于</a></li>
    {% if username %}
        <li class="layui-nav-item" style="float: right; margin-right: 30px;">
            <a href="javascript:;">{{ name }}</a>
            <dl class="layui-nav-child">
                <dd><a href="/blog/myBlog">我的博客</a></dd>
                <dd><a href="/blog/myComment">我的评论</a></dd>
                <dd><a href="/logout">注销</a></dd>
            </dl>
        </li>
        <li class="layui-nav-item{% block write_class %}{% endblock %}" style="float: right"><a href="/blog/writeBlog">写博客</a></li>
    {% else %}
        <li class="layui-nav-item{% block register_class %}{% endblock %}" style="float: right"><a href="/register">注册</a></li>
        <li class="layui-nav-item{% block login_class %}{% endblock %}" style="float: right"><a href="/login">登录</a></li>
    {% endif %}
</ul>

<div class="content">
    {% block content %}
        {# 其他页面内容 #}
    {% endblock %}
</div>

<script>
layui.use('element', function(){
    var element = layui.element;
});
</script>
</body>
</html>

这里页面使用了Layui定义了一个导航栏,展示了对应的功能模块。其中{% if username %},username为后台存放在session中的一个键值对,用于判断用户是否登录了,有些功能登录后才显示。

base.html模板文件完成后,我们在定义一个index.html来做项目的首页,直接继承base.html。这样首页index.html就节省了很多代码。如下:

{% extends 'base.html' %}

{% block title %}
    在线博客平台
{% endblock %}

{% block content %}
    <h1 style="margin: 35vh;">在线博客平台</h1>
{% endblock %}

首页效果如下:

登录与注册功能

登录

先定义一个登录的视图函数,可以接收GET、POST请求,GET请求为跳转到登录页面,POST请求为处理登录提交的请求,验证是否登录成功,登录成功后把当前登录对象的用户名存入session会话中。

# 登录请求
@index.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        user = User.query.filter(User.username == username).first();
        # check_password_hash比较两个密码是否相同
        if (user is not None) and (check_password_hash(user.password, password)):
            session['username'] = user.username
            session.permanent = True
            return redirect(url_for('index.hello'))
        else:
            flash("账号或密码错误")
            return render_template('login.html');

登录页面是用Layui写的一组form表单,也是基础的base.html,代码如下:

{% extends 'base.html' %}

{% block title %}
    在线博客平台.登录
{% endblock %}

{% block css %}
<link rel="stylesheet" href="/static/css/register.css">
{% endblock %}

{% block content %}
    <div class="register">
        <h1>登录</h1>
        <p class="tip">
            {% for item in get_flashed_messages() %}
            {{ item }}
            {% endfor %}
        </p>
        <form class="layui-form" action="login" method="post">
            <div class="layui-form-item">
                <label class="layui-form-label">用户名</label>
                <div class="layui-input-block">
                    <input type="text" name="username" required  lay-verify="required" placeholder="请输入用户名" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">密  码</label>
                <div class="layui-input-block">
                    <input type="password" name="password" required lay-verify="required" placeholder="请输入密码" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn" lay-submit lay-filter="formDemo">立即提交</button>
                    <button type="reset" class="layui-btn layui-btn-primary">重置</button>
                </div>
            </div>
        </form>
    </div>
    <script>
        layui.use('form', function(){
            var form = layui.form;
            form.on('submit(formDemo)', function(data){
            });
        });
    </script>
{% endblock %}

{% block login_class %}
    layui-this
{% endblock %}

效果如下(账号和密码错误后,会有相应的提示信息):
注册和登录差不多,页面都是使用的同一个css样式文件,所以这里就贴代码出来了,需要的可以自行下载完整项目代码:GitHub地址。

修改密码

修改密码模块,因为数据库存放明文密码很不安全,所以这里使用了Werkzeug对密码进行了加密存储。对于WerkZeug密码加密想进一步了解的,可以访问Flask 使用Werkzeug实现密码加密。

因为数据库中存储的是加密后的密码,所以这里判断原密码是否正确需要使用check_password_hash函数进行判断。

定义一个修改密码的视图函数。

# 修改密码
@index.route("/updatePwd", methods=['POST', 'GET'])
@login_limit
def update():
    if request.method == "GET":
        return render_template("updatePwd.html")
    if request.method == 'POST':
        lodPwd = request.form.get("lodPwd")
        newPwd1 = request.form.get("newPwd1")
        newPwd2 = request.form.get("newPwd2")
        username = session.get("username");
        user = User.query.filter(User.username == username).first();
        if check_password_hash(user.password, lodPwd):
            if newPwd1 != newPwd2:
                flash("两次新密码不一致!")
                return render_template("updatePwd.html")
            else:
                user.password_hash(newPwd2)
                db.session.commit();
                flash("修改成功!")
                return render_template("updatePwd.html")
        else:
            flash("原密码错误!")
            return render_template("updatePwd.html")

页面样式文件和登录注册引入的样式文件一致(原密码不正确或两次新密码不同,会给出相应的提示信息),代码如下:

{% extends 'base.html' %}

{% block title %}
    在线博客平台.修改密码
{% endblock %}

{% block css %}
<link rel="stylesheet" href="/static/css/register.css">
{% endblock %}

{% block content %}
    <div class="register">
        <h1>修改密码</h1>
        <p class="tip">
            {% for item in get_flashed_messages() %}
            {{ item }}
            {% endfor %}
        </p>
        <form class="layui-form" action="updatePwd" method="post">
            <div class="layui-form-item">
                <label class="layui-form-label">原密码</label>
                <div class="layui-input-block">
                    <input type="password" name="lodPwd" required  lay-verify="required" placeholder="请输入原密码" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">新密码</label>
                <div class="layui-input-block">
                    <input type="password" name="newPwd1" required lay-verify="required" placeholder="请输入新密码" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">确认新密码</label>
                <div class="layui-input-block">
                    <input type="password" name="newPwd2" required lay-verify="required" placeholder="请再次输入新密码" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn" lay-submit lay-filter="formDemo">立即提交</button>
                </div>
            </div>
        </form>
    </div>
    <script>
        layui.use('form', function(){
            var form = layui.form;
            form.on('submit(formDemo)', function(data){
            });
        });
    </script>
{% endblock %}

{% block updatepwd_class %}
    layui-this
{% endblock %}

效果如下:

写博客

写博客,博客表中会保存标题、博客内容、当前时间等字段。如下是写博客的视图函数。

# 写博客页面
@blog.route('/writeBlog', methods=['POST', 'GET'])
@login_limit
def writeblog():
    if request.method == 'GET':
        return render_template('writeBlog.html')
    if request.method == 'POST':
        title = request.form.get("title")
        text = request.form.get("text")
        username = session.get('username')
        # 获取当前系统时间
        create_time = time.strftime("%Y-%m-%d %H:%M:%S")
        user = User.query.filter(User.username == username).first()
        blog = Blog(title=title, text=text, create_time=create_time, user_id=user.id)
        db.session.add(blog)
        db.session.commit();
        blog = Blog.query.filter(Blog.create_time == create_time).first();
        return render_template('blogSuccess.html', title=title, id=blog.id)

保存博客时会获取到当前系统时间,当做博客的发布时间。博客保存成功后,会返回保存成功页面,下面会有讲解。

写博客对应的html文件,代码如下。

{% extends 'base.html' %}

{% block title %}
    在线博客平台.写博客
{% endblock %}

{% block css %}
<link rel="stylesheet" type="text/css" href="/static/editor/css/editormd.css"/>
<script src="/static/editor/editormd.js" type="text/javascript"></script>
{% endblock %}

{% block content %}
    <div class="main">
        <form action="/blog/writeBlog" class="layui-form" method="post">
            <div class="layui-form-item">
                <label class="layui-form-label">标   题</label>
                <div class="layui-input-block">
                    <input type="text" name="title"  lay-verify="required" placeholder="请输入标题" class="layui-input">
                </div>
            </div>
            <div id="editormd">
                <textarea name = "text" lay-verify="required" style="display:none;" ></textarea>
            </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn" style="width: 150px" lay-submit lay-filter="formDemo">保存</button>
                </div>
            </div>
        </form>
    </div>

    <script type="text/javascript">
        layui.use('form', function(){
            var form = layui.form;
            form.on('submit(formDemo)', function(data){
            });
        });

        $(function() {
            editormd("editormd", {
                width: "100%",
                height: 600,
                syncScrolling: "single",
                path: "/static/editor/lib/", //依赖lib文件夹路径
                emoji: true,
                taskList: true,
                tocm: true,
                imageUpload: true, //开启本地图片上传
                imageFormats: ["jpg", "jpeg", "gif", "png"], //设置上传图片的格式
                imageUploadURL: "/blog/imgUpload"  //上传图片请求路径
            });
        });
    </script>
{% endblock %}

{% block write_class %}
    layui-this
{% endblock %}

写博客这里采用的是Markdown编辑器,对于Markdown编辑器之前写过一篇Markdown的使用方法,只不过后端用的是Java语言,感兴趣的小伙伴可以看看,Markdown的基本使用。Flask与之不同的是,后端接收Markdown上传图片时的语句不同,Flask接收Markdown上传图片的语句:

file = request.files.get('editormd-image-file');

其他的基本相同,毕竟Markdown是属于前端的知识,后端只要求根据规定个格式返回数据即可。

因为Markdown支持图片上传,那就必须的有文件上传的方法了。如下定义一个文件上传的视图函数(这里需要注意的是Markdown上传图片是使用的POST方法)。

# 上传图片
@blog.route('/imgUpload', methods=['POST'])
@login_limit
def imgUpload():
    try:
        file = request.files.get('editormd-image-file');
        fname = secure_filename(file.filename);
        ext = fname.rsplit('.')[-1];
        # 生成一个uuid作为文件名
        fileName = str(uuid.uuid4()) + "." + ext;
        filePath = os.path.join("static/uploadImg/", fileName);
        file.save(filePath)
        return {
            'success': 1,
            'message': '上传成功!',
            'url': "/" + filePath
        }
    except Exception:
        return {
            'success': 0,
            'message': '上传失败'
        }

如果对上述的文件上传代码比较陌生,可以访问Flask 文件上传与下载,对Flask文件上传与下载进一步了解。

效果如下:

保存成功后,会返回保存成功页面,可以在写一篇,或者查看当前发布的文章。

查看博客列表

查看博客列表就是遍历所有已发布的博客。先定义一个视图函数,查询所有已发布的博客,传递到前端进行遍历显示。视图函数代码如下:

# 展示全部博客
@blog.route("/blogAll")
def blogAll():
    # order_by按照时间倒序
    blogList = Blog.query.order_by(Blog.create_time.desc()).all();
    return render_template('blogAll.html', blogList=blogList)

因为最新发布的博客在数据库的最后一条,所以这里根据发布时间倒序查询。

页面代码如下:

{% extends 'base.html' %}

{% block title %}
    在线博客平台.博客
{% endblock %}

{% block css %}
<link rel="stylesheet" href="/static/css/blogAll.css">
{% endblock %}

{% block content %}
    <div class="main">
        <ul>
            {% for blog in blogList %}
                <li>
                    <a class="title" href="/blog/showBlog/{{ blog.id }}">{{ blog.title }}</a>
                    <p>
                        发布人:{{ blog.user.name }} &nbsp;&nbsp;&nbsp;&nbsp;发布时间:{{ blog.create_time }}
                    </p>
                </li>
            {% endfor %}
        </ul>
    </div>
{% endblock %}

{% block blog_class %}
    layui-this
{% endblock %}

效果如下:

博客详情页面

在博客列表中点击博客的标题可以进入博客的详情页面,详情页面展示了博客的详细内容以及评论内容。

因为数据库中保存博客内容的是Markdown格式的,所以在这里需要解析成HTML格式,解析代码如下。

<script src="/static/editor/lib/marked.min.js"></script>
<script src="/static/editor/lib/prettify.min.js"></script>
<script src="/static/editor/lib/raphael.min.js"></script>
<script src="/static/editor/lib/underscore.min.js"></script>
<script src="/static/editor/lib/sequence-diagram.min.js"></script>
<script src="/static/editor/lib/flowchart.min.js"></script>
<script src="/static/editor/lib/jquery.flowchart.min.js"></script>
<script src="/static/editor/editormd.js"></script>
editormd.markdownToHTML("test", {
    htmlDecode: "style,script,iframe",
    emoji: true,
    taskList: true,
    tex: true,  // 默认不解析
    flowChart: true,  // 默认不解析
    sequenceDiagram: true  // 默认不解析
});

评论

在博客详情页面可以进行评论,评论使用的是Layui的编辑器,比较简约也可以达到想要的效果。

看上去是不是还可以,和页面也很搭。评论需要先登录才可以评论,如果没有登录则会提示登录。

如果登录评论后,会发送保存评论请求,携带当前博客的id和评论内容进行保存。
保存评论的视图函数。

# 评论
@blog.route("/comment", methods=['POST'])
@login_limit
def comment():
    text = request.values.get('text')
    blogId = request.values.get('blogId')
    username = session.get('username')
    # 获取当前系统时间
    create_time = time.strftime("%Y-%m-%d %H:%M:%S")
    user = User.query.filter(User.username == username).first()
    comment = Comment(text=text, create_time=create_time, blog_id=blogId, user_id=user.id)
    db.session.add(comment)
    db.session.commit();
    return {
        'success': True,
        'message': '评论成功!',
    }

上述的博客内容解析与评论都在一个页面中,完整代码如下。

{% extends 'base.html' %}

{% block title %}
    在线博客平台.博客
{% endblock %}

{% block css %}
<link rel="stylesheet" type="text/css" href="/static/editor/css/editormd.css"/>
<link rel="stylesheet" href="/static/css/showBlog.css">
<script src="/static/editor/lib/marked.min.js"></script>
<script src="/static/editor/lib/prettify.min.js"></script>
<script src="/static/editor/lib/raphael.min.js"></script>
<script src="/static/editor/lib/underscore.min.js"></script>
<script src="/static/editor/lib/sequence-diagram.min.js"></script>
<script src="/static/editor/lib/flowchart.min.js"></script>
<script src="/static/editor/lib/jquery.flowchart.min.js"></script>
<script src="/static/editor/editormd.js"></script>
{% endblock %}

{% block content %}
    <div class="main">
        <h1>{{ blog.title }}</h1>
        <p>发布人:{{ blog.user.name }} &nbsp;&nbsp;&nbsp;&nbsp;发布时间:{{ blog.create_time }}</p>
        <hr>
        <div id="test">
            <textarea>{{ blog.text }}</textarea>
        </div>
        <fieldset class="layui-elem-field layui-field-title">
            <legend>发表评论</legend>
            <input type="hidden" id="blog_id" name="blogId" value="{{ blog.id }}">
            <textarea id="lay_edit" lay-verify="content" name="text"></textarea>
            <button type="button" class="layui-btn comSub">提交评论</button>
        </fieldset>
        <hr style="margin-top: 30px; margin-bottom: 20px;">
        <ul class="comment">
            {% for com in comment %}
                <li>
                    <p class="myText">{{ com.text }}</p>
                    <p>评论人:{{ com.user.name }} &nbsp;&nbsp;&nbsp;&nbsp;发布时间:{{ com.create_time }}</p>
                </li>
            {% endfor %}
        </ul>
    </div>

    <script type="text/javascript">
        $(function (){
            $(".myText").each(function () {
                $(this).html($(this).text());
            });
        })

        editormd.markdownToHTML("test", {
            htmlDecode: "style,script,iframe",
            emoji: true,
            taskList: true,
            tex: true,  // 默认不解析
            flowChart: true,  // 默认不解析
            sequenceDiagram: true  // 默认不解析
        });

        layui.use(['layedit', 'form'], function () {
            var form = layui.form;
            var layedit = layui.layedit;
            //创建一个编辑器
            var index = layedit.build('lay_edit', {
                height: 150,
                tool: [
                    'face', //表情
                    '|', //分割线
                    'link' //超链接
                ]
            });
            $(".comSub").click(function (){
                layui.use('layer', function(){
                    var layer = layui.layer;
                    {% if username %}
                        //获取评论内容
                        var text = layedit.getContent(index);
                        var blogId = $("#blog_id").val();
                        if(text == "" || text == undefined){
                            layer.msg("评论不能为空哦!", {icon: 0});
                        }else {
                            $.post("/blog/comment", {text: text, blogId: blogId}, function (result) {
                                if (result.success) {
                                    window.location.href = '/blog/showBlog/' + blogId;
                                }
                            })
                        }
                    {% else %}
                        layer.confirm('登录后在评论哦!', {
                            btn: ['取消','登录']
                        }, function(index){
                            layer.close(index);
                        }, function(){
                            window.location.href = '/login';
                        });
                    {% endif %}
                });
            })
        });
    </script>
{% endblock %}

我的博客

登录之后在右上角导航栏可以查看我的博客,查看个人已经发布过的博客并进行管理。

定义一个视图函数,查询当前登录的用户发布的所有博客。

# 查看个人博客
@blog.route("/myBlog")
@login_limit
def myBlog():
    username = session.get('username')
    user = User.query.filter(User.username == username).first()
    # order_by按照时间倒序
    blogList = Blog.query.filter(Blog.user_id == user.id).order_by(Blog.create_time.desc()).all()
    return render_template("myBlog.html", blogList=blogList)

页面与博客列表基本相似,但可以对其博客进行修改与删除。

修改博客

在我的博客中,有修改博客的链接,把当前的博客id当做参数传递到后台,查询当前这条博客的数据,进行修改。

# 博客修改
@blog.route("/update/<id>", methods=['POST', 'GET'])
@login_limit
def update(id):
    if request.method == 'GET':
        blog = Blog.query.filter(Blog.id == id).first();
        return render_template('updateBlog.html', blog=blog)
    if request.method == 'POST':
        id = request.form.get("id")
        title = request.form.get("title")
        text = request.form.get("text")
        blog = Blog.query.filter(Blog.id == id).first();
        blog.title = title;
        blog.text = text;
        db.session.commit();
        return render_template('blogSuccess.html', title=title, id=id)

修改页面和写博客的页面基本一样,在textarea标签中设置markdown编辑器的默认值。

<textarea name = "text" lay-verify="required" style="display:none;" >{{ blog.text }}</textarea>

删除博客

删除博客和修改一样,把博客的id传到后端,根据id删除数据库中对应的数据。

# 删除博客
@blog.route("/delete/<id>")
@login_limit
def delete(id):
    blog = Blog.query.filter(Blog.id == id).first();
    db.session.delete(blog);
    db.session.commit();
    return {
        'state': True,
        'msg': "删除成功!"
    }

删除成功后,使用JS删除页面上对应的DOM元素。

 function del(url, that){
    layui.use('layer', function(){
        var layer = layui.layer;
        layer.confirm('您确定要删除吗?', {
            btn: ['取消','确定']
        }, function(index){
            layer.close(index);
        }, function(){
            $.get(url, function (data){
                if(data.state){
                    $(that).parent().parent().parent().remove();
                    layer.msg(data.msg, {icon: 1});
                }
            })
        });
    });
}

我的评论

在页面的右上角不仅可以查看个人已发布的博客,也可以看到自己的所有评论信息。

根据评论列表,可以点击评论或博客,可以进入评论的博客详情页中;也可以对评论的内容进行删除操作。

定义一个视图函数,查询所有的评论内容,返回给前台遍历展示(同样根据时间倒序查询)。

# 用户所有的评论
@blog.route('/myComment')
@login_limit
def myComment():
    username = session.get('username')
    user = User.query.filter(User.username == username).first()
    # order_by按照时间倒序
    commentList = Comment.query.filter(Comment.user_id == user.id).order_by(Comment.create_time.desc()).all();
    return render_template("myComment.html", commentList=commentList)

前端页面展示代码。

{% extends 'base.html' %}

{% block title %}
    在线博客平台.我的评论
{% endblock %}

{% block css %}
<link rel="stylesheet" href="/static/css/blogAll.css">
{% endblock %}

{% block content %}
    <div class="main">
        <ul>
            {% for comment in commentList %}
                <li>
                    <a class="title" href="/blog/showBlog/{{ comment.blog_id }}">{{ comment.text }}</a>
                    <p>
                        博客:<a href="/blog/showBlog/{{ comment.blog_id }}">{{ comment.blog.title }}</a> &nbsp;&nbsp;&nbsp;&nbsp;评论时间:{{ comment.create_time }}
                        <span class="operation">
                            <a href="javascript:;" onclick="del('/blog/deleteCom/{{ comment.id }}', this)">删除</a>
                        </span>
                    </p>
                </li>
            {% endfor %}
        </ul>
    </div>

    <script type="text/javascript">
         $(function (){
            $(".title").each(function () {
                $(this).html($(this).text());
            });
        })

        function del(url, that){
            layui.use('layer', function(){
                var layer = layui.layer;
                layer.confirm('您确定要删除吗?', {
                    btn: ['取消','确定']
                }, function(index){
                    layer.close(index);
                }, function(){
                    $.get(url, function (data){
                        if(data.state){
                            $(that).parent().parent().parent().remove();
                            layer.msg(data.msg, {icon: 1});
                        }
                    })
                });
            });
        }
    </script>
{% endblock %}

页面样式和博客列表样式一致。

删除评论

在评论列表中有删除评论的链接,根据评论的id删除当前条评论,删除后,对应博客中的评论也随之删除。

# 删除评论
@blog.route('/deleteCom/<id>')
def deleteCom(id):
    com = Comment.query.filter(Comment.id == id).first()
    db.session.delete(com);
    db.session.commit();
    return {
        'state': True,
        'msg': "删除成功!"
    }

关于页面

关于页面可以简单的描述一下网站的设计及作用等,这里就没有写过多的内容了,可以自行设计。

注销

注销只需要清除session中的数据,返回首页即可。

# 退出
@index.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index.hello'))

定义错误页面

系统在平时使用中难免会遇到一些错误,但又不能直接让用户看到这些错误,所以我们可以定义一个错误页面,使其报错后都跳转到此页面。Flask中有两个视图函数处理404和500错误的,这里直接使用即可,这里两个视图函数都是跳转到了同一个页面(也可以跳转不同的页面)。

# 404页面
@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404;

# 500页面
@app.errorhandler(500)
def internal_server_error(e):
    return render_template('404.html'), 500;

错误页面这里就简单的插入了一张图片,添加了一个返回首页的链接。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>网站走丢了。。。</title>
</head>
<style type="text/css">
body{
    position: fixed;
    width: 100%;
    height: 100vh;
    background: url('/static/img/404.gif') no-repeat;
    background-size: cover;
    z-index: -1;
}
a{
    width: 65px;
    display: inherit;
    margin: 0 auto;
    margin-top: 87vh;
    padding: 5px 20px;
    border: 1px solid;
    border-radius: 8px;
}
</style>
<body>
<a href="/">返回首页</a>
</body>
</html>

效果如下:

源码下载

到这里整个博客系统就完成了,最后在附上下载链接:https://github.com/machaoyin/flask_blog/tree/master

版权声明:本文为CSDN博主「编程 小马」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:

https://blog.csdn.net/qq_40205116/article/details/110265729

往期推荐

用 Python 写了一个学生在线考试管理系统

Python课程设计:学生成绩管理系统(附源代码)

用Python写了一个疫苗信息管理系统

用 Python 写了一个青年大学习提醒系统

< END >

微信扫码关注,了解更多内容

以上是关于博客系统前端实现的主要内容,如果未能解决你的问题,请参考以下文章

Web前端期末大作业--零食商城网页设计(HTML+CSS+JavaScript+)实现

Web前端期末大作业--零食商城网页设计(HTML+CSS+JavaScript+)实现

实现一个博客系统(前端页面设计)

学生HTML个人网页作业作品 使用HTML+CSS+JavaScript个人介绍博客网站 web前端课程设计 web前端课程设计代码 web课程设计

❤️响应式性感美女模特博客网站模板❤️(HTML+CSS+JavaScript-前端大作业)

❤️响应式性感美女模特博客网站模板❤️(HTML+CSS+JavaScript-前端大作业)