社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
本章目标:添加“点赞功能”功能
github: 打开后,点击右上角star按钮
视频教程: B站地址
简书专集:点我
文章详情页面的点赞功能有"文章点赞"和"评论点赞",因为点赞的大体逻辑是一致的,文章点赞和评论点赞只是更新不同的表的“点赞数量”字段,因此我们定义同一控制器方法来处理点赞功能。这儿,我们就需要请求参数“type”来区分是文章的点赞还是评论的点赞,我们同时还需要知道是对文章或评论的key开确定是对那一条记录的点赞。还有情况,当用户点过赞的时候,再次点赞的时候,我们需要提示用户已经点过赞,因此我们需要添加点赞流水表,记录点赞成功的流水,方便判断用户是否已经点过赞。
...
<div class="container container-message container-details">
<div class="contar-wrap">
<div class="item">
...
<!--此处为文章点赞按钮,因此data-type为note ,将文章的key赋值给data-key -->
<span class="pull-right like" data-type="note" data-key="{{.note.Key}}">
<i class="layui-icon layui-icon-praise"></i>
<!--添加样式value,方便后期修改点赞数量 -->
<em class="value">{{.note.Praise}}</em>
</span>
...
</div>
<div id="LAY-msg-box">
...
<!--此处为评论点赞按钮,因此data-type为message ,将评论的key赋值给data-key -->
<span class="info-img like" data-type="message" data-key="{{.Key}}">
<i class="layui-icon layui-icon-praise"></i>
<!--span是添加的,样式为value,方便后期修改点赞数量 -->
<span class="value">{{.Praise}}</span>
</span>
...
</div>
</div>
</div>
...
//这个代码在126行左右,将 $(".like").on('click',praise)中的praice方法独立出来,原代码是匿名函数。这样是为了后期留言的点赞的事件绑定。
$(function () {
$(".like").on('click',praise);
});
function praise() {
if(!($(this).hasClass("layblog-this"))){
//取刚才我们修改details.html时,定义好的data-type的值
var type = $(this).data("type");
//取刚才我们修改details.html时,定义好的data-key的值
var key = $(this).data("key");
// that保存好当前的this,方便回调函数里面使用
var that = this;
//向后台发送请求 路径为/praise/:type/:key
$.post("/praise/"+type+"/"+key,function (data) {
if(data.code ==0 ){
//如果点赞成功 的逻辑
that.text = '已赞';
$(that).addClass('layblog-this');
$.tipsBox({
obj: $(that),
str: "+1",
callback: function () {
}
});
niceIn($(that));
layer.msg('点赞成功', {
icon: 6
,time: 1000
})
//上面都是点赞的提示和动画,下面的是赋值点赞数量,如果点赞成功后台会把点赞数量带过来的。
$(that).find(".value").text(data.praise)
}else{
//我们后台需要定义 特殊的错误类型code为4444,代表已经点过赞,
if (data.code ==4444){
//将点过赞的样式添加到点赞按钮上去
$(that).addClass('layblog-this');
layer.msg(data.msg);
}else{
layer.msg(data.msg);
}
}
}).error(function () {
layer.msg("网络异常");
});
}
}
type HasPraiseError struct {
UnKnowError
}
func (err HasPraiseError) Error() string {
return "您已经点过赞!"
}
func (err HasPraiseError) Code() int {
return 4444
}
type PraiseLog struct {
Model
UserId int //点赞用户id
Key string //文章或评论的key
Table string// 点赞的表名(评论为messages,文章为notes)
Flag bool //是否点赞
}
func init() {
...
//添加&PraiseLog{}
db.AutoMigrate(&User{},&Note{},&Message{},&PraiseLog{})
..
}
//说明:文章表和评论表都有key和praise,他们的key是唯一的,所以我们可以根据表名和key开确定是哪条记录,做具体的更新点赞数量的操作。
//这个结果体是为了方便查询 已经点赞的数量
type TempPraise struct {
Praise int
}
//核心方法
func UpdatePraise(table ,key string ,userid int )(pcnt int,err error) {
//开启事务
d := db.Begin()
//判断如果函数返回错误不为空,就事务回滚。
defer func() {
if err != nil {
//回滚事务
d.Rollback()
} else {
//提交事务
d.Commit()
}
}()
//查询点赞流水表,看是否有记录,并赋值给p,
//如果有记录,我们判断下Flag是否为true,如果为true就是点赞,就提示已经点赞的错误。
//如果没有,我们就重新赋值一个flag为false的点赞流水,赋值给p
var p PraiseLog
err = d.Model(&PraiseLog{}).Where("`key` = ? and `table` =? and user_id =? ", key, table, userid).Take(&p).Error
if err == gorm.ErrRecordNotFound {
// 如果查询不到数据 我们就赋值 Flag为false的点赞流水给p
p = PraiseLog{
Key: key,
Table: table,
UserId: userid,
Flag: false,
}
} else if err != nil {
// 如果其他的错误,就返回错误
return 0, err
}
// 如果flag为true,说明已经点赞过,我们就提示已经点赞的错误
if p.Flag {
// HasPraiseError是我们定义的错误类型,code为4444,代表已经点赞
return 0, syserror.HasPraiseError{}
}
//更新点赞,为true。
p.Flag = true
//保存 流水
if err = d.Save(&p).Error; err != nil {
return 0, err
}
//更新文章或留言表的点赞数量
var ppp TempPraise
err = d.Table(table).Where("key = ?", key).Select("praise").Scan(&ppp).Error
if err != nil {
return 0, err
}
pcnt = ppp.Praise+1
if err = d.Table(table).Where("key = ? ",key).Update("praise",pcnt).Error;err!=nil{
return 0, err
}
// 返回点赞数量
return pcnt, nil
}
//定义控制器 PraiseController
type PraiseController struct {
BaseController
}
//实现 NextPrepare接口,每次请求都必须登陆,没有登陆提示错误
func (this *PraiseController) NextPrepare() {
this.MustLogin()
}
//定义路由
//@router /:type/:key [post]
func (this *PraiseController) Praise() {
// 获取页面传过来的type,用来区分是文章还是评论
ttype:= this.Ctx.Input.Param(":type")
// 获取页面传过来的key,文章或评论的key
key:= this.Ctx.Input.Param(":key")
// 定义table变量,
table :="notes"
// 根据ttype的不同,指定不同的表
switch ttype {
case "message":
table="messages"
case "note":
table="notes"
default:
// 不是文章或评论,就是提示 “未知类型”错误
this.Abort500(errors.New("未知类型"))
}
// 调用我们刚才定义的更新点赞的方法。
pcnt,err:=models.UpdatePraise(table,key,int(this.User.ID))
if err!=nil{
//如果报错,我们得先判断是不是 已经点赞过的错误,如果是,我们放回点赞过的的错误
if e2,ok:=err.(syserror.HasPraiseError);ok{
this.Abort500(e2)
}
//我们重新定义 “点赞失败”,将具体的错误原因显示在日志里面
this.Abort500(syserror.New("点赞失败",err))
}
//点赞成功,返回点赞数量
this.JSONOkH("点击成功",H{"praise":pcnt})
}
文章详情页面的点赞,我们已经实现。
首页的文章列表的点赞,比较简单,因为我们已经实现后台点赞的核心逻辑,首页的文章列表点赞只是缺少data-type和data-like这个两个属性值,其余的都没有问题,我们只需调整views->index.html页面,即可实现点赞功能。
<div class="item">
...
<a href="javascript:;" class="like" data-type="note" data-key="{{.Key}}">点赞</a>
...
</div>
首页的文章列表的点赞,我们已经实现
留言页面的点赞,后台逻辑也已经实现,我们也只需调整前台页面即可,首先留言页面有两处问题,js的模版需要调整,也要添加data-type和data-like属性。还有一处调整是,因为留言的的数据是在页面加载好后,用ajax再次获取,重新画出留言列表的。所以我们需要在ajax加载成功的回调函数里面,添加绑定点赞事件的代码。而留言的新增也是一样。
...
{{str2html `
<script id="LAY-msg-tpl" type="text/html">
<div class="info-box">
<div class="info-item">
<img class="info-img" src="{{ d.avatar }}" alt="">
<div class="info-text">
<!-- 此处为代码说明,使用的时候删掉 。添加了样式count,这是因为layblog-this这个样式需要上级元素含有样式“count”才能正常显示 -->
<p class="title count">
<span class="name">{{ d.username }}</span>
<!-- 此处为代码说明,使用的时候删掉 。添加了样式like,方便事件绑定,添加了data-type和data-key属性 -->
<span class="info-img like"
data-type="message"
data-key="{{ d.key}}">
<i class="layui-icon layui-icon-praise"></i>
<!-- 此处为代码说明,使用的时候删掉 。添加了span元素并指定样式为value,方便赋值点赞数量 -->
<span class="value">{{ d.praise }}</span>
</span>
</p>
<p class="info-intr">
{{ d.content }}
</p>
</div>
</div>
</div>
</script>
`}}
...
laypage.render({
...
, jump: function (obj, first) {
...
$.get("/message/query", ..., function (ret) {
if (ret.code == 0) {
...
//绑定 点赞按钮的点击事件
$html.find(".like").on("click",praise);
...
} ...
})...
}
});
$('#item-btn').on('click', function(){
...
$.post("/message/new",...,function (ret) {
if(ret.code==0){
...
// 下面三句话,用来绑定 点赞按钮的点击事件。
var $html = $(html);
$html.find(".like").on("click",praise);
$('#LAY-msg-box').prepend($html);
...
}...
})...
});
留言页面的点赞,我们已经实现
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!