基于Springboot的个人博客系统的设计与实现
基于Springboot的个人博客系统的设计与实现 可行性分析3.1 系统需求分析
3.1.1 功能需求分析
(1)用户管理
注册与登录:支持游客和博主注册账号,并提供密码找回功能。
用户资料编辑:允许游客和博主编辑个人资料,包括头像、昵称、简介等。
权限管理:区分游客与博主权限,管理员可管理所有用户、文章等。
(2)文章管理
文章发布:博主能够编写、格式化文章,并添加标题、标签、封面图等。
文章编辑与删除:游客和博主可对自己的文章进行编辑和删除,博主可以编辑删除。
文章分类与标签:支持文章分类和标签功能,便于用户查找和浏览。
文章搜索:提供全文搜索功能,用户可根据关键词搜索文章。
(3)评论与互动
评论功能:读者可以对文章进行评论,支持回复评论形成讨论。
评论管理:用户能管理自己文章的评论,管理员可管理全站评论。
点赞与分享:支持对文章和评论点赞,以及通过社交媒体分享文章。
(4)统计与报告
访问统计:提供网站访问量、用户行为分析等数据。
文章分析:显示文章的阅读量、点赞数、评论数等统计信息。
3.1.2 性能需求分析
响应速度:确保系统在高并发访问下依然能快速响应,页面加载时间不超过3秒。
可扩展性:系统设计需考虑未来用户增长和功能扩展的需求,易于水平扩展。
稳定性:系统需具备高可用性,确保长时间稳定运行,减少宕机时间。
3.1.3 安全性需求分析
用户认证与授权:采用安全的认证机制(如OAuth2、JWT),确保用户身份安全。
数据加密:对敏感数据(如用户密码)进行加密存储和传输。
防止SQL注入:使用预处理语句或ORM框架防止SQL注入攻击。
XSS与CSRF防护:实施适当的防御措施,防止跨站脚本攻击和跨站请求伪造。
日志记录与监控:记录用户行为和系统异常,便于问题追踪和安全审计。
3.1.4 可用性需求分析
界面友好:设计简洁、直观的用户界面,提供良好的用户体验。
多平台支持:确保系统能在多种设备(PC、平板、手机)和浏览器上良好运行。
易用性:功能布局合理,操作流程简单明了,减少用户学习成本。
3.1.5 维护与升级
文档齐全:提供详尽的开发文档和用户手册,便于后续维护和升级。
版本控制:采用版本控制系统(如Git)管理代码,确保代码可追溯性和可管理性。
3.2 系统可行性分析
3.2.1 经济可行性分析
本系统构建于一系列开源技术框架之上,包括但不限于Spring、Spring MVC、MyBatis以及Spring Boot,同时前端采用了Vue.js。数据库层面,我们选用了同样开源且免费的MySQL数据库系统。在开发工具的选择上,我们利用了IntelliJ IDEA,它为学生用户提供了免费使用的许可,且鉴于本项目仅由我一人负责开发,因此也享受到了免费的权益。基于上述所有组件均为开源或免费获取,本系统的经济成本极低,从而在经济可行性评估中,可以明确判断为完全可行。
3.2.2 技术可行性分析
本系统采用了Maven作为项目管理及依赖管理工具,确保了项目构建和依赖管理的高效与准确。在数据库交互层面,本系统选用了MyBatis作为ORM框架,它与MySQL数据库紧密协作,实现了高效、灵活的数据访问。对于处理来自浏览器的请求,本系统则采用了经过广泛验证的Spring MVC框架,该框架以其强大的功能、高度的可配置性和优秀的性能,确保了请求处理的流畅与准确。Maven、MyBatis、Spring MVC以及MySQL这四者均是在市场上经过长时间检验的成熟技术,它们在安全性、可用性、可靠性等方面均展现出了卓越的表现,因此可以肯定地说,本系统在这些关键维度上均具备坚实的基础和可靠的保障。
3.2.3 操作可行性分析
个人博客系统开发是结合个人需求而开发,能解决用户可以通过互联网来展现自己等诸多问题,因此该项目符合开发条件,具有成熟的基础,并且,从前面的分析来看,技术上的操作是十分成熟并且开源免费广为人们使用,从系统的操作来看上完全是可行的。C:\Users\阮如意\Desktop 4 系统设计
4.1 系统总流程
如图4.1所示,本个人博客系统分为两个角色,分别是博主和游客。只有博主才能登录后台管理系统,进行博客发表。
(1)首先要通过用户名和密码登录后台管理系统,才能发布博客。
(2)博主和游客都可以阅读博客。
(3)博主和游客都可以发表评论。
(4)经过博主的评论审核,评论才可以发布。
(5)此时博主和游客才可以观看到评论。
C:\Users\阮如意\Desktop
图4.1系统流程图
4.2 博主用例
博主用例如图4.2所示。
图4.2博主用例图
博主的功能包括:
(1)登入功能
首先进入登录页面,需要输入账号和密码。它会使用Shiro进行安全管理,对前台输入的密码进行加密运算,然后与数据库中的进行比较。成功后才能登入后台系统。
(2)博客管理功能
博客管理功能分为写博客和博客信息管理。写博客是博主用来发表编写博客的,需要博客标题,然后选择博客类型,最后将博客内容填入百度的富文本编辑器中,点击发布博客按钮即可发布博客。
(3)博客类别管理系统
博主类别管理系统可以添加,修改和删除博客类型名称和排序序号。将会显示到首页的按日志类别区域。游客可以从这里查找相关的感兴趣的博客内容。
(4)评论管理功能
评论管理功能分为评论审核和评论信息管理两部分。评论审核是当有游客或自己发表了评论之后,博主需要在后台管理系统中审核评论。若想将此评论显示在页面上则点击审核通过,否则点击审核不通过。
(5)个人信息管理功能
在这里可以修改博主的个人信息,可以修改昵称,个性签名,可以添加个人头像,修改个人简介。
(6)系统管理功能
这里的功能有友情链接管理,修改密码,刷新系统缓存和安全退出。友情链接管理可以添加,修改,删除友情链接网址。
4.3 游客用例
游客用例如图4.3所示。
图4.3游客用例图
游客功能包括:
(1)查询博客
在查询搜索处填写所需的条件,点击查询,便会显示出符合条件的所有博客
(2)查看博客内容
点击想要查看的博客,进入博客内容页面,观看博客内容信息。
(3)查看博主个人信息
点击关于博主,进入博主个人信息页面,即可看到博主的个人信息内容。
(4)发表评论
在发表评论的评论框中写入自己想要发表的评论,点击发表评论
(5)查看友情链接
在每个页面的右下角可以看到友情链接,若想跳转到某个友情链接则点击相应的友情链接即可观看友情链接内容。
4.4 系统类
本系统主要功能和模块的JavaBean主要集中博客、博客类型、评论、友情链接这四个类。
4.4.1 博客类
博客类如图4.4所示。
图4.4博客类图
(1)功能
用于存储博主发表的博客的一些信息。
(2)属性
id; // 编号
title; // 博客标题
summary; // 摘要
leaseDate; // 发布日期
clickHit; // 查看次数
replyHit; // 回复次数
content; // 博客内容
contentNoTag; // 博客内容 无网页标签 Lucene分词用
blogType; // 博客类型
blogCount; // 博客数量,非博客实际属性,主要根据发布日期归档查询博客数量用
releaseDateStr; // 发布日期字符串只取年和月
keyWord; // 关键字
4.4.2 博主类
(1)功能
用于存储博主信息。
(2)属性
id; // 编号
userName; // 用户名
password; // 密码
nickName; // 昵称
sign; // 个性签名
proFile; // 个人简介
imageName; // 博主头像
4.4.3 评论类
(1)功能
对博客评论数据保存。
(2)属性
id; // 编号
userIp; // 用户IP
content; // 评论内容
blog; // 被评论的博客
commentDate; // 评论日期
state; // 审核状态0 待审核 1 审核通过 2 审核未通过
4.4.4 友情链接类
(1)功能
可以保存页面上的友情链接。
(2)属性
id; // 编号
linkName; // 链接名称
linkUrl; // 链接地址
orderNo; // 排序序号 从小到大排序
4.5 数据库设计
4.5.1 E-R图
(1)博客
博客类的关系模式(加下滑线的是主键),如图4.5所示:
博客(编号,博客标题,摘要,发布日期,查看次数,博客类型,关键字,博客内容)。
图4.5博客E-R图
(2)博主
博主类的关系模式如下(加下滑线的是主键),如图4.6所示:
博主实体(编号,用户名,密码,昵称,个性签名,个人简介,博主头像)。
图4.6博主E-R图
(3)博客类型
博客类型类的关系模式如下(加下滑线的是主键),如图4.7所示:
博客类型实体(编号,博客类型名称,数量,排序)。
图4.7博客类型E-R图
(4)评论
评论类的关系模式如下(加下滑线的是主键),如图4.8所示:
评论实体(编号,用户IP,评论内容,被评论的博客,评论日期,审核状态)。
图4.8评论E-R图
(5)友情链接
友情链接类的关系模式如下(加下滑线的是主键),如图4.9所示:
友情链接实体(编号,链接名称,链接地址,排序序号)。
图4.9友情链接E-R图
4.5.2 系统表设计
系统总共有五张表,分别是博客表,博主表,博客类型表,评论表,友情链接表,具体字段参考E-R图。
表4-1 博客表
字段名 数据类型 允许非空
id int(11) no
title varchar(200) yes
summary varchar(400) yes
releaseDate datetime yes
clickHit int(11) yes
replyHit int(11) yes
content text yes
typeId int(11) yes
表4-2 博主表
字段名 数据类型 允许非空
id int(11) no
userName varchar(50) yes
password varchar(100) yes
profile text yes
nickName varchar(50) yes
sign varchar(100) yes
imageName varchar(100) yes
表4-3 博客类型表
字段名 数据类型 允许非空
id int(11) no
typeName varchar(30) yes
orderNo int(11) yes
表4-4 评论表
字段名 数据类型 允许非空
id int(11) no
userIp varchar(50) yes
blogId int(11) yes
content varchar(1000) yes
commentDate datetime yes
state int(11) yes
表4-5 友情链接表
字段名 数据类型 允许非空
id int(11) no
linkName varchar(100) yes
linkUrl varchar(200) yes
orderNo int(11) yes 系统实现登录模块5.1.1 博主登录登入系统后台管理登录页面,博主首先输入用户名和密码,它回去调用Controller层代码,然后进入业务层调用数据库的一些操作确认是否用户名密码正确,然后返回到前台就会登进去。具体代码见附录。vue代码:使用form表单提交到后台进行数据验证。java代码:使用MVC框架,对后台数据和前台form表单提交的数据进行交互。5.1.2 游客登录与博主类似。博客管理模块博客管理管理模块为个人博客系统的用户(即博主)提供写博客和博客信息管理功能。在个人博客系统的首页上的博客就是从这里进行发布的。博客管理包含新建:対博客的新建,博客可以含有图片,视频,音频附件。新建博客必须要有博客标题,博客类别自己选择所需要的博客类别,然后填入博客内容,最后发表文章。搜索:在博客信息管理中,可以输入自己想要搜索的博客信息,系统会自动筛选出适合的博客展现给用户。修改:点击你想要修改的博客,系统会弹出修改博客页面,之后博客的标题,所属的博客类型,博客内容等都可以修改。删除:该系统支持单个删除和批量删除。5.2.1 博客查询时序图如图5.1所示。图5.1查询博客时序图按条件查询博客,调用控制层的list方法,在其中调用服务层list方法,返回blog集合:List<Blog> list。获得集合后使用方法把查询到符要求的数据传到前台,在前台对数据进行处理。5.2.2 博客新建时序图如图5.2所示。图5.2新建博客时序图点击写博客进入博客发布界面,填写博客内容信息,点击发布博客,提交到控制层的save方法,传入参数:Blog,该参数包含了该博客所有信息内容,在提交的时候使用javascript对博客的内容进行校验。校验成功后便会调用控制层的方法,在控制层的save()方法中调用服务层的add()方法,对该博客进行保存,持久到数据库中。5.2.3 博客修改时序图如图5.3所示。图5.3修改博客时序图进入博客修改页面后,修改博客的内容信息,点击发布博客,提交到控制层的save()方法,传入参数:Blog,该参数包含了修改后的博客所有信息内容,在提交的时候使用javascript对博客的内容进行校验。校验成功后便会调用控制层的方法,在控制层的save()方法中调用服务层的update()方法,对该博客进行保存,持久到数据库中。5.2.4 博客删除在博客管理页面选择需要删除的博客,点击删除按钮,提交到控制层的delete方法,传入参数:需要删除博客的id,调用控制层的方法,在控制层的delete()方法中调用服务层的delete()方法,对该博客或多个博客进行删除,持久到数据库中。时序图如图5.4所示。图5.4删除博客时序图博客类别管理模块5.3.1 添加博客类别时序图如图5.5所示。图5.5添加博客类型时序图在博客类别管理页面打开添加博客类别弹窗,填写博客类别名称和排序,点击保存按钮,提交到控制层的save()方法,传入参数:需要添加的博客类型blogType,调用控制层的方法,在控制层的save()方法中调用服务层的add()方法,添加博客类别的相关信息,持久到数据库中。5.3.2 修改博客类别时序图如图5.6所示。图5.6修改博客类型时序图在博客类别管理页面先选择要修改的数据,点击修改按钮,打开修改博客类别弹窗,填写修改后博客类别名称和排序,点击保存按钮,提交到控制层的save方法,传入参数:需要添加的博客类型blogType,调用控制层的方法,在控制层的save()方法中调用服务层的update()方法,修改博客类别的相关信息,持久到数据库中。5.3.3 删除博客类别在博客类别管理页面先勾选上要删除的数据,点击删除按钮,提交到控制层的delete方法,传入参数:需要删除的博客类型的id(可以是多个),调用控制层的方法,在控制层的delete()方法中调用服务层的delete()方法,删除博客类别的相关信息,持久到数据库中。时序图如图5.7所示。图5.7删除博客类型时序图5.3.4 显示博客类别时序图如图5.8所示。图5.8显示博客类型时序图在打开博客类别管理页面时,调用控制层的list方法,传入参数:当前页数和每页数量,调用控制层的方法,在控制层的list()方法中调用服务层的list()方法,从数据库中查询到博客类别信息,然后将博客类别的相关数据渲染页面上。评论管理模块5.4.1 评论博主在后台页面打开评论管理页面,选择需要审核的评论,点击审核通过按钮,调用controller层的review方法,controller层调用service层的update方法,将评论的状态字段改为对应的代表通过审核的1,如果点击不通过那就是2,然后调用dao层的update方法持久化到数据库中。时序图如图5.9所示。图5.9审核评论时序图5.4.2 删除评论博主在后台打开评论管理页面,选择需要删除的评论,点击删除按钮。调用controller层的delete方法,在其中的方法中调用业务逻辑层的实现方法,然后调用dao层的方法,最后持久化到数据库中,相关的代码如下:时序图如图5.10所示。图5.10删除评论时序图 系统测试主页面主页面,当用户在浏览器的网址输入框中输入正确的地址就可以看到系统前台页面,如图6.1所示。图6.1主页面登录注册界面用户在前台首页可以点击登录后台按钮可以登录到后台管理页面,在后台管理页面可以进行博客管理,博客类别管理,评论审核和个人信息管理还有系统管理。但是首先要获取用户名和密码,拥有博主权限才可以进入到后台管理页面,首先输入用户名:admin,然后输入密码: 123。结果有两种,一种是密码正确成功登录,另外一种是不成功,登录失败,若是失败,则会在页面上打出红色字体:用户名或密码错误,如图6.2所示。图6.2登陆注册界面后台管理界面登录成功后,可以进入到后台管理页面,在这个页面,博主可以查看平台总体情况,如图6.3所示。也可以管理博客,新增博客,删除修改博客,管理评论等等,如图6.4,图6.5,图6.6所示。图6.3平台情况界面图6.4博客管理界面图6.5博客添加界面图6.6评论管理界面博客查看界面用户以游客或博主登录后可以搜索查看任意博客,也都可以评论,如图6.7,图6.8所示。图6.7博客搜索界面图6.8博客具体界面及评论相关界面除了这些基本界面,我还设计并实现了更多相关界面,使得该个人博客系统更加完善,功能更加全面。展示如下。图6.9随笔界面(博主均可写)图6.10留言界面(所有用户均可写)图6.11管理员用户管理界面 部分代码://留言管理界面<template><div class="app-container"> <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px"> <el-form-item label="评论者" prop="content" v-if="isAdmin"> <el-input v-model="queryParams.createBy" placeholder="请输入评论者" clearable size="small" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="评论内容" prop="content"> <el-input v-model="queryParams.content" placeholder="请输入评论内容" clearable size="small" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="文章标题" prop="blogTitle"> <el-input v-model="queryParams.blogTitle" placeholder="请输入文章标题" clearable size="small" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> </el-form-item> </el-form> <!-- <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['cms:comment:add']" >新增</el-button> </el-col> <el-col :span="1.5"> <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['cms:comment:edit']" >修改</el-button> </el-col> <el-col :span="1.5"> <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['cms:comment:remove']" >删除</el-button> </el-col> <el-col :span="1.5"> <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['cms:comment:export']" >导出</el-button> </el-col> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> </el-row> <el-table v-loading="loading" :data="commentList" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column label="ID" align="center" prop="id" /> <el-table-column label="父评论id" align="center" prop="parentId" /> <el-table-column label="主评论id(第一级评论)" align="center" prop="mainId" /> <el-table-column label="点赞数量" align="center" prop="likeNum" /> <el-table-column label="内容" align="center" prop="content" /> <el-table-column label="评论类型:对人评论,对项目评论,对资源评论" align="center" prop="type" /> <el-table-column label="被评论者id,可以是人、项目、资源" align="center" prop="blogId" /> <el-table-column label="评论者id" align="center" prop="userId" /> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['cms:comment:edit']" >修改</el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['cms:comment:remove']" >删除</el-button> </template> </el-table-column> </el-table> --> <el-card> <div class="el-card__header" style="text-align: left;padding: 0"> <h3 style="float: left;margin: 0;">评论列表</h3> <right-toolbar style="float: right;" :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> </div> <ul style="padding: 0;" class="comment-list"> <li v-show="commentList.length==0" style="text-align: center;border-bottom: 1px dashed #ccc;margin: 10px 0;list-style-type:none;"> <span class="el-table__empty-text">暂无数据</span> </li> <li class="comment" v-for="mes in commentList" :key="mes.id"> <el-avatar v-if="mes.avatar!==''&&mes.avatar!=null" :src="mes.avatar"></el-avatar> <el-avatar v-else icon="el-icon-user-solid"></el-avatar> <div class="content"> <div style="display: flex;justify-content: space-between;width: 100%"> <div class="nkname"> <span class="name">{{mes.createBy}} </span> <span class="date">{{mes.createTime}}</span> <span v-show="mes.type=='0'" class="rp">给你评论</span> <span v-show="mes.type=='1'" class="rp">回复了</span> <span v-show="mes.type=='1'" class="pcreateBy">{{mes.pcreateBy}}</span> <span v-show="mes.type=='1'" class="rp">的评论</span> <span class="rp">|</span> <span class="rp">查看文章:</span> <span class="blog" @click="getBlogInfo(mes)">{{mes.blogTitle}}</span> </div> <div class="op"> <span @click="getBlogInfo(mes)" class="blog">查看</span> <span> | </span> <el-button type="text" @click="handleAdd(mes)" v-hasPermi="['cms:comment:add']">回复</el-button> <span v-show="!isAdmin&&mes.createBy!=name" style="margin-right: 39.43px;"></span> <span v-show="!(!isAdmin&&mes.createBy!=name)"> | </span> <span v-show="!(!isAdmin&&mes.createBy!=name)" class="del" @click="handleDelete(mes)">删除</span> </div> </div> <p class="reply">{{mes.content}}</p> </div> </li> </ul> </el-card> <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> <!-- 添加或修改评论管理对话框 --> <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> <el-form ref="form" :model="form" :rules="rules" label-width="80px"> <el-input @blur="blur" v-model="form.content" type="textarea" maxlength="100" show-word-limit :placeholder="toName" /> </el-form> <div slot="footer" class="dialog-footer"> <div style="float: left;"> <Emoji @output="output"></Emoji> </div> <el-button type="primary" @click="submitForm">确 定</el-button> <el-button @click="cancel">取 消</el-button> </div> </el-dialog></div></template><script>import { mapGetters} from 'vuex'import { listComment, getComment, delComment, addComment, updateComment } from "@/api/cms/comment";import { Loading} from 'element-ui';import Emoji from '@/components/Emoji'export default {name: "Comment",components: { Emoji},data() { return { // 遮罩层 loading: true, // 选中数组 ids: [], names: [], // 非单个禁用 single: true, // 非多个禁用 multiple: true, // 显示搜索条件 showSearch: false, // 总条数 total: 0, // 评论管理表格数据 commentList: [], // 弹出层标题 title: "", // 是否显示弹出层 open: false, // 查询参数 queryParams: { pageNum: 1, pageSize: 10, parentId: null, mainId: null, likeNum: null, content: null, type: null, blogId: null, blogTitle: null, userId: null, delFlag: null, createBy: null, }, // 表单参数 form: {}, // 表单校验 rules: { }, toName:'', isAdmin: false, cursorIndexStart: null,//光标选中开始的位置 cursorIndexEnd: null,//光标选中结束的位置 };},computed: { ...mapGetters([ 'name' ]),},created() {},mounted: function () { this.$nextTick(function () { // 仅在整个视图都被渲染之后才会运行的代码 this.getList(); this.isAdminRole(); })},methods: { /** 查询评论管理列表 */ getList() { // this.loading = true; let loadingInstance = Loading.service({ target: ".comment-list" }); listComment(this.queryParams).then(response => { for (let i = 0; i < response.rows.length; i++) { let mesInfo = response.rows; if (mesInfo.avatar != null && mesInfo.avatar != "") { response.rows.avatar = process.env.VUE_APP_BASE_API + mesInfo.avatar } }; this.commentList = response.rows; this.total = response.total; // this.loading = false; setTimeout(() => { loadingInstance.close(); }, 100); }); }, // 取消按钮 cancel() { this.open = false; this.reset(); }, // 表单重置 reset() { this.form = { id: null, parentId: null, mainId: null, likeNum: null, content: null, type: null, blogId: null, delFlag: null, userId: null, createBy: null, createTime: null, updateBy: null, updateTime: null }; this.resetForm("form"); }, /** 搜索按钮操作 */ handleQuery() { this.queryParams.pageNum = 1; this.getList(); }, /** 重置按钮操作 */ resetQuery() { this.queryParams.createBy = ""; this.queryParams.content = ""; this.resetForm("queryForm"); this.handleQuery(); }, // 多选框选中数据 handleSelectionChange(selection) { this.ids = selection.map(item => item.id) this.single = selection.length!==1 this.multiple = !selection.length }, /** 新增按钮操作 */ handleAdd(mes) { this.reset(); if(mes.mainId!=null){ this.form.mainId = mes.mainId }else{ this.form.mainId = mes.id } this.form.parentId = mes.id this.form.blogId = mes.blogId this.form.type = '1' this.form.createBy = this.$store.getters.name this.toName = "@" + mes.createBy this.open = true; this.title = "回复评论"; }, /** 修改按钮操作 */ handleUpdate(row) { this.reset(); const id = row.id || this.ids getComment(id).then(response => { this.form = response.data; this.open = true; this.title = "修改评论管理"; }); }, /** 提交按钮 */ submitForm() { this.$refs["form"].validate(valid => { if (valid) { if (this.form.id != null) { updateComment(this.form).then(response => { this.$modal.msgSuccess("修改成功"); this.open = false; this.getList(); }); } else { addComment(this.form).then(response => { this.$modal.msgSuccess("回复成功"); this.open = false; this.getList(); }); } } }); }, /** 删除按钮操作 */ handleDelete(row) { const ids = row.id || this.ids; let name = row.content || this.names; this.$modal.confirm('是否确认删除 "' + name + '" ?').then(function() { return delComment(ids); }).then(() => { this.getList(); this.$modal.msgSuccess("删除成功"); }).catch(() => {}); }, /** 导出按钮操作 */ handleExport() { this.download('cms/comment/export', { ...this.queryParams }, `comment_${new Date().getTime()}.xlsx`) }, // 跳转到评论页 getBlogInfo(mes) { let routeUrl = this.$router.resolve({ path: '/cms/main/blog', query: { id: mes.blogId, commentId: mes.id } }); window.open(routeUrl.href, '_blank'); }, isAdminRole(){ // 验证用户是否具备某角色 if(this.$auth.hasRole("admin")||this.$auth.hasRole("cms")){ this.isAdmin = true; } }, blur(e){ this.cursorIndexStart = e.srcElement.selectionStart// 获取input输入框失去焦点时光标选中开始的位置 this.cursorIndexEnd = e.srcElement.selectionEnd// 获取input输入框失去焦点时光标选中结束的位置 }, output(val) { if (this.cursorIndexStart !== null && this.form.content) { //如果 文本域获取了焦点, 则在光标位置处插入对应字段内容 this.form.content = this.form.content.substring(0, this.cursorIndexStart) + val + this.form.content.substring(this.cursorIndexEnd) } else { // 如果 文本域未获取焦点, 则在字符串末尾处插入对应字段内容 this.form.content = this.form.content?this.form.content:'' + val } },}};</script><style scoped>.comment { border-bottom: 1px dashed #ccc; margin: 10px 0; display: flex;}.el-avatar { width: 35px; height: 35px; /* border: 2px solid white; */ box-shadow: 0 0 10px 2px rgba(0, 0, 0, .06); flex-shrink: 0;}.content { text-align: left; font-size: 14px; flex-grow: 1;}.nkname { margin-left: 10px; max-width: 830px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;}.rp,.date { color: #999; margin-left: 10px;}.pcreateBy { margin-left: 10px;}.blog { color: #349edf; margin-left: 10px; cursor: pointer;}.reply { margin-left: 10px;}.op { color: #ddd; font-weight: lighter;}.rep { color: #349edf;}.del { color: red;}.op:hover { cursor: pointer;}.el-table__empty-text { line-height: 60px; width: 50%; color: #909399;}</style> 部分代码:/登陆注册界面<template><div class="login"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"> <h3 class="title">个人博客系统</h3> <el-form-item prop="username"> <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号" > <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /> </el-input> </el-form-item> <el-form-item prop="password"> <el-input v-model="loginForm.password" type="password" auto-complete="off" placeholder="密码" @keyup.enter.native="handleLogin" > <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /> </el-input> </el-form-item> <el-form-item prop="code" v-if="captchaOnOff"> <el-input v-model="loginForm.code" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter.native="handleLogin" > <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /> </el-input> <div class="login-code"> <img :src="codeUrl" @click="getCode" class="login-code-img"/> </div> </el-form-item> <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> <el-form-item style="width:100%;"> <el-button :loading="loading" size="medium" type="primary" style="width:100%;" @click.native.prevent="handleLogin" > <span v-if="!loading">登 录</span> <span v-else>登 录 中...</span> </el-button> <div style="float: right;" v-if="register"> <router-link class="link-type" :to="'/register'">立即注册</router-link> </div> </el-form-item> </el-form> <!--底部--></div></template><script>import { getCodeImg } from "@/api/login";import Cookies from "js-cookie";import { encrypt, decrypt } from '@/utils/jsencrypt'export default {name: "Login",data() { return { codeUrl: "", loginForm: { username: "admin", password: "admin123", rememberMe: false, code: "", uuid: "" }, loginRules: { username: [ { required: true, trigger: "blur", message: "请输入您的账号" } ], password: [ { required: true, trigger: "blur", message: "请输入您的密码" } ], code: [{ required: true, trigger: "change", message: "请输入验证码" }] }, loading: false, // 验证码开关 captchaOnOff: true, // 注册开关 register: false, redirect: undefined };},watch: { $route: { handler: function(route) { this.redirect = route.query && route.query.redirect; }, immediate: true }},created() { this.getCode(); this.getCookie();},methods: { getCode() { getCodeImg().then(res => { this.captchaOnOff = res.captchaOnOff === undefined ? true : res.captchaOnOff; if (this.captchaOnOff) { this.codeUrl = "data:image/gif;base64," + res.img; this.loginForm.uuid = res.uuid; } }); }, getCookie() { const username = Cookies.get("username"); const password = Cookies.get("password"); const rememberMe = Cookies.get('rememberMe') this.loginForm = { username: username === undefined ? this.loginForm.username : username, password: password === undefined ? this.loginForm.password : decrypt(password), rememberMe: rememberMe === undefined ? false : Boolean(rememberMe) };}, handleLogin() { this.$refs.loginForm.validate(valid => { if (valid) { this.loading = true; if (this.loginForm.rememberMe) { Cookies.set("username", this.loginForm.username, { expires: 30 }); Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 }); Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 }); } else { Cookies.remove("username"); Cookies.remove("password"); Cookies.remove('rememberMe'); } this.$store.dispatch("Login", this.loginForm).then(() => { this.$router.push({ path: this.redirect || "/" }).catch(()=>{}); }).catch(() => { this.loading = false; if (this.captchaOnOff) { this.getCode(); } }); } }); }}};</script><style rel="stylesheet/scss" lang="scss">.login {display: flex;justify-content: center;align-items: center;height: 100%;background-image: url("../assets/images/login-background.jpg");background-size: cover;}.title {margin: 0px auto 30px auto;text-align: center;color: #707070;}.login-form {visibility: hidden;background: #ffffff;width: 400px;padding: 25px 25px 5px 25px;.el-input { height: 38px; input { height: 38px; }}.input-icon { height: 39px; width: 14px; margin-left: 2px;}}.login-tip {font-size: 13px;text-align: center;color: #bfbfbf;}.login-code {width: 33%;height: 38px;float: right;img { cursor: pointer; vertical-align: middle;}}.el-login-footer {height: 40px;line-height: 40px;position: fixed;bottom: 0;width: 100%;text-align: center;color: #fff;font-family: Arial;font-size: 12px;letter-spacing: 1px;}.login-code-img {height: 38px;}</style>
页:
[1]