文章点赞功能(Ajax)

一、文章点赞样式构建

1、将base.html的css样式改为外部引入

  将base.html的内嵌样式删除,改为使用 HTML 头部的 <head> 标签对中使用<link>标签来引入外部的 CSS 文件。

base.html内容如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 引入 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css">
    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="/static/js/jquery-3.3.1.js"></script>
    <!-- 引入 Bootstrap 核心 JavaScript 文件 -->
    <script src="/static/blog/bootstrap-3.3.7/js/bootstrap.js"></script> <!--依赖jquery-->
    <link rel="stylesheet" href="/static/blog/css/home_site.css">
    <link rel="stylesheet" href="/static/blog/css/article_detail.css">
</head>
<body>

<div class="header">
    <div class="content">
        <!--站点标题-->
        <p class="title">
            <span>{{ blog.title }}</span>
            <a href="" class="backend">管理</a>
        </p>
    </div>
</div>

<div class="container">
    <div class="row">
        <div class="col-md-3">
            <!--添加bootstrap面板-->
            {% load my_tags %}
            {% get_classification_style username %}
        </div>
        <div class="col-md-9">
            {% block content %}

            {% endblock %}
        </div>
    </div>
</div>

</body>
</html>

 个人站点的样式——home_site.css:

* {
    margin: 0;
    padding: 0;
}
.header {
    width: 100%;
    height: 60px;
    background-color: #369;
}
.header .title {
    font-size: 18px; /* 字体大小 */
    font-weight: 100; /* 字体粗细 */
    line-height: 60px; /* 行高与页头一致,完成居中 */
    color: white;
    margin-left: 15px;
    margin-top: -10px;
}
.backend {
    float: right; /* 浮动到右边 */
    color: white;
    text-decoration: none; /* 去除下划线 */
    font-size: 16px;
    margin-right: 12px;
    margin-top: 10px;
}
.pub_info {
    margin-top: 10px;
    color: darkgray;
}
.menu {
    margin-top: 20px;
}

文章详情页的样式——article_detail.css:

.article_info .title {
    margin-bottom: 20px;
}

  上述css代码是将标题部分和文字主体部分错开20像素。

2、构建点赞样式

  根据博客园代码,在article_detail.html引入文章推荐踩灭:

{% extends "base.html" %}

{% block content %}
    <h3 class="text-center">{{ article_obj.title }}</h3>
    <div class="cont">
        {{ article_obj.content|safe }}
    </div>

    {# 文章点赞 #}
    <div id="div_digg">
        <div class="diggit">
            <span class="diggnum" id="digg_count">1</span>
        </div>
        <div class="buryit">
            <span class="diggnum" id="bury_count">0</span>
        </div>
        <div class="clear"></div>
        <div class="diggword" id="digg_tips" style="color: red;"></div>
    </div>
{% endblock %}

  将点赞的css样式写入article_detail.css中:

.article_info .title {
    margin-bottom: 20px;
}

#div_digg {
    float: right;
    margin-bottom: 10px;
    margin-right: 30px;
    font-size: 12px;
    width: 125px;
    text-align: center;
    margin-top: 10px;
}

/* 推荐 */
.diggit {
    float: left;
    width: 46px;
    height: 52px;
    background: url('/static/font/upup.gif') no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}

/* 反对 */
.buryit {
    float: right;
    margin-left: 20px;
    width: 46px;
    height: 52px;
    background: url('/static/font/downdown.gif') no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}

.clear {
    clear: both;  /* 清除浮动,解决塌陷问题 */
}

  显示效果:

  

 

二、文章点赞事件绑定 (Ajax)

  给推荐和反对的这两个标签绑定事件,一点击就发送ajax请求。

  另外查看blog_articleupdown表:

  

  其中is_up字段是存的是一个布尔值。点击推荐则为True,点击反对则为False.在这里将两个标签合在一个事件中,仅仅是做判断点击的是哪个标签。注意文章点赞的script代码应写在article_detail.html模板中。

  由于这两个标签有不同的类,一个包含diggit,一个包含buryit。因此只需要判断点击的标签class名就可以,article_detail.html:

 {% extends "base.html" %}

{% block content %}
    <h3 class="text-center">{{ article_obj.title }}</h3>
    <div class="cont">
        {{ article_obj.content|safe }}
    </div>

    {# 文章点赞 #}
    <div id="div_digg">
        {# 推荐 #}
        <div class="diggit action">
            <span class="diggnum" id="digg_count">1</span>
        </div>
        {# 点灭 #}
        <div class="buryit action">
            <span class="diggnum" id="bury_count">0</span>
        </div>
        <div class="clear"></div>
        <div class="diggword" id="digg_tips" style="color: red;"></div>
    </div>

    <script>
        $('#div_digg .action').click(function () {
            var is_up = $(this).hasClass("diggit");
            alert(is_up);
        })
    </script>
{% endblock %}

三、文章点赞保存

1、在article_detail.html模板初步构建ajax请求

<script>
    $('#div_digg .action').click(function () {
        var is_up = $(this).hasClass("diggit");
        $.ajax({
            url: "/digg/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                "is_up": is_up,
                "article_id": "{{ article_obj.pk }}",
            },
            success: function (data) {
                console.log(data);
            }
        })
    })
</script>

  需要注意:文章点赞人、评论人都应是当前登录人。因此这里不需要传入user_id到ajax中。

  由于这里是post请求,因此需要添加csrf_token避免forbidden错误,因此需要构建一条新路由:

urlpatterns = [
    path('admin/', admin.site.urls),
    ...
    path('digg/', views.digg),  # 点赞
  ... ]

  创建点赞视图函数:

def digg(request):
    """
    点赞视图函数
    :param request:
    :return:
    """
    print(request.POST)

    return HttpResponse("OK")

  访问页面,点击推荐,视图函数request.POST输出:

<QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'], 'is_up': ['true'], 'article_id': ['1']}>

  同时页面控制台输出:OK。

2、生成赞记录

import json

def digg(request):
    """
    点赞视图函数
    :param request:
    :return:
    """
    print(request.POST)
    # <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'],
    #               'is_up': ['true'], 'article_id': ['1']}>

    article_id = request.POST.get("article_id")
    # is_up = request.POST.get("is_up")    # 拿到的是一个字符串 "true"
    is_up = json.loads(request.POST.get("is_up"))   # 反序列化,拿到一个bool值

    # 点赞人即当前登录人
    user_id = request.user.pk

    # 创建一条新记录
    ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)

    return HttpResponse("OK")

在这里需要注意:

(1)生成一条赞记录根据models中的类:

  

  因此拿到article_id、user_id、is_up这三条就可以生成一个新记录。

(2)request.POST.get("is_up") 拿到的结果是一个字符串。因为它在js中虽然也是bool值,但是在传递时,没有设置ContentType,默认使用urlencoded编码,在组装键值的时候,is_up = True就按照字符串发送出去了。因此需要json来进行反序列化。

(3)点击赞后,查看blog_articleupdwon表记录,确认新记录是否生成:

  

四、文章点赞数的数据同步

  生成一条赞记录,就应该把赞记录对应的文章up_count+1,如果是踩灭,则将down_count+1;保持这种同步。

  涉及到up_count自加一,需要用到Django的F函数。

import json

from django.db.models import F   # F函数

def digg(request):
    """
    点赞视图函数
    :param request:
    :return:
    """
    print(request.POST)
    # <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'],
    #               'is_up': ['true'], 'article_id': ['1']}>

    article_id = request.POST.get("article_id")
    # is_up = request.POST.get("is_up")    # 拿到的是一个字符串 "true"
    is_up = json.loads(request.POST.get("is_up"))   # 反序列化,拿到一个bool值

    # 点赞人即当前登录人
    user_id = request.user.pk

    # 创建一条新记录
    ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)

    queryset = models.Article.objects.filter(pk=article_id)
    if is_up:
        queryset.update(up_count=F("up_count")+1)
    else:
        queryset.update(up_count=F("down_count")+1)

    return HttpResponse("OK")

  工作原理是,直接在数据库中查出数据,计数后更改数据库。点赞后刷新页面,点赞次数已经更新:

  

五、点赞提示重复操作

  博客园文章点赞规则:用户对一篇文章点赞后,不允许再对文章进行点赞或踩灭操作。

1、根据用户在点赞是否有记录进行判断

import json
from django.http import JsonResponse

from django.db.models import F   # F函数

def digg(request):
    """
    点赞视图函数
    :param request:
    :return:
    """
    print(request.POST)
    # <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'],
    #               'is_up': ['true'], 'article_id': ['1']}>

    article_id = request.POST.get("article_id")
    # is_up = request.POST.get("is_up")    # 拿到的是一个字符串 "true"
    is_up = json.loads(request.POST.get("is_up"))   # 反序列化,拿到一个bool值

    # 点赞人即当前登录人
    user_id = request.user.pk

    # 用户只要在点赞表存有记录就不能再存了
    obj = models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()

    response = {"state": True}
    if not obj:
        # 创建一条新记录
        ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)

        queryset = models.Article.objects.filter(pk=article_id)
        if is_up:
            queryset.update(up_count=F("up_count")+1)
        else:
            queryset.update(down_count=F("down_count")+1)
    else:
        # 重复点赞提示,告诉ajax已经推荐过了
        response["state"] = False
        response["handled"] = obj.is_up   # True:推荐过了, Flase: 踩过了

    return JsonResponse(response)   # 返回response字典

  注意:

(1)通过models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first(),拿到当前用户在点赞表中针对该文章的点赞记录。if not obj则可以判定没有点赞过,可以正常操作。

(2)提前创建response字典,将需要传递给ajax的信息放入其中。然后引入JsonResponse传递字典。

2、在ajax中处理重复错误信息

<script>
    $('#div_digg .action').click(function () {
        var is_up = $(this).hasClass("diggit");
        $.ajax({
            url: "/digg/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                "is_up": is_up,
                "article_id": "{{ article_obj.pk }}"
            },
            success: function (data) {
                console.log(data);
                if (data.state) {

                } else {
                    if (data.handled) {
                        $("#digg_tips").html("您已经推荐过!")
                    } else {
                        $("#digg_tips").html("您已经反对过!")
                    }
                    setTimeout(function () {
                        $("#digg_tips").html("")
                    }, 1000)
                }
            },
        })
    })
</script>

  注意:

(1)在data.state为false时,判断data.handled字典是True/False,在id="digg_tips"标签添加对应的内容。

(2)运用setTimeout函数,在提示消息显示1秒后,自动隐藏。

(3)显示效果如下:

  

六、点赞数ajax更新

  修改article_detail.html内script代码:

<script>
    $('#div_digg .action').click(function () {
        var is_up = $(this).hasClass("diggit");
        $.ajax({
            url: "/digg/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                "is_up": is_up,
                "article_id": "{{ article_obj.pk }}"
            },
            success: function (data) {
                console.log(data);
                if (data.state) {
                    if (is_up){
                        var val = parseInt($("#digg_count").text());  // parseInt() 函数可解析一个字符串,并返回一个整数。
                        $("#digg_count").text(val+1);
                    } else {
                        var val = parseInt($("#bury_count").text());
                        $("#bury_count").text(val+1);
                    }

                } else {
                    if (data.handled) {
                        $("#digg_tips").html("您已经推荐过!")
                    } else {
                        $("#digg_tips").html("您已经反对过!")
                    }
                    setTimeout(function () {
                        $("#digg_tips").html("")
                    }, 1000)
                }
            },
        })
    })
</script>

  这样在点击推荐或踩灭后,不用刷新页面,第一时间就显示了数字加1。

七、代码优化

  主要针对article_detail.html中js的重复代码。

{% extends "base.html" %}

{% block content %}
    {%  csrf_token %}
    <h3 class="text-center">{{ article_obj.title }}</h3>
    <div class="cont">
        {{ article_obj.content|safe }}
    </div>

    {# 文章点赞 #}
    <div id="div_digg">
        {# 推荐 #}
        <div class="diggit action">
            <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
        </div>
        {# 点灭 #}
        <div class="buryit action">
            <span class="diggnum" id="bury_count">{{ article_obj.down_count }}</span>
        </div>
        <div class="clear"></div>
        <div class="diggword" id="digg_tips" style="color: red;"></div>
    </div>

<script>
    $('#div_digg .action').click(function () {
        var is_up = $(this).hasClass("diggit");

        $obj = $(this).children("span");

        $.ajax({
            url: "/digg/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                "is_up": is_up,
                "article_id": "{{ article_obj.pk }}"
            },
            success: function (data) {
                console.log(data);
                if (data.state) {
                    var val = parseInt($obj.text());  // parseInt() 函数可解析一个字符串,并返回一个整数。
                    $obj.text(val+1);
                } else {
                    // 三元表达式
                    var val = data.handled?"您已经推荐过!":"您已经反对过!";
                    $("#digg_tips").html(val);

                    setTimeout(function () {
                        $("#digg_tips").html("")
                    }, 1000)
                }
            },
        })
    })
</script>
{% endblock %}

  注意:

(1)$obj = $(this).children("span");  变量定义,取消了is_up的判断。

(2)应用三元表达式true对应已经推荐过,false对应已经反对过

// 三元表达式
var val = data.handled?"您已经推荐过!":"您已经反对过!";

  

 

转载于:https://www.cnblogs.com/xiugeng/p/9426091.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 创建点赞表 在MySQL数据库中创建一个点赞表,用于存储点赞信息。该表的结构可以如下所示: ```sql CREATE TABLE `like_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `target_id` int(11) NOT NULL, `type` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` 其中,id为点赞记录的唯一标识,user_id为点赞用户的id,target_id为点赞目标的id,type为点赞类型,例如点赞的是文章、评论还是其他类型的内容。 2. 实现点赞功能 在Java中使用MyBatis框架实现点赞功能,需要创建Mapper接口和XML文件来实现数据库操作。 (1)创建Mapper接口 在Mapper接口中定义点赞功能的方法,例如: ```java public interface LikeMapper { /** * 根据用户id和目标id查询点赞记录 * @param userId 用户id * @param targetId 目标id * @return 点赞记录 */ LikeInfo getLikeInfo(@Param("userId") int userId, @Param("targetId") int targetId); /** * 添加点赞记录 * @param likeInfo 点赞记录 * @return 影响的行数 */ int addLikeInfo(LikeInfo likeInfo); /** * 删除点赞记录 * @param id 点赞记录的id * @return 影响的行数 */ int deleteLikeInfo(int id); } ``` (2)创建XML文件 在XML文件中实现Mapper接口中定义的方法,例如: ```xml <mapper namespace="com.example.mapper.LikeMapper"> <resultMap id="likeInfoMap" type="com.example.entity.LikeInfo"> <id column="id" property="id"/> <result column="user_id" property="userId"/> <result column="target_id" property="targetId"/> <result column="type" property="type"/> </resultMap> <select id="getLikeInfo" resultMap="likeInfoMap"> SELECT * FROM like_info WHERE user_id = #{userId} AND target_id = #{targetId} </select> <insert id="addLikeInfo" parameterType="com.example.entity.LikeInfo"> INSERT INTO like_info(user_id, target_id, type) VALUES(#{userId}, #{targetId}, #{type}) </insert> <delete id="deleteLikeInfo"> DELETE FROM like_info WHERE id = #{id} </delete> </mapper> ``` 3. 在JSP页面中实现点赞功能 在JSP页面中实现点赞功能,需要使用Ajax技术向服务器发送请求,并根据服务器返回的数据更新页面上的点赞状态和点赞数。 (1)向服务器发送点赞请求 可以在页面上添加一个点赞按钮,当用户点击该按钮时,使用Ajax向服务器发送点赞请求。例如: ```javascript function like() { $.ajax({ type: "POST", url: "/like", data: { targetId: ${targetId}, type: ${type} }, dataType: "json", success: function (result) { if (result.code == 200) { // 更新点赞状态和点赞数 } else { alert(result.msg); } }, error: function () { alert("点赞失败!"); } }); } ``` 其中,targetId和type是要点赞的目标的id和类型,可以从后端传递到页面上。 (2)处理点赞请求 后端可以使用Spring MVC框架来处理点赞请求,首先需要定义一个Controller来处理请求。例如: ```java @RestController public class LikeController { @Autowired private LikeService likeService; @PostMapping("/like") public Result like(@RequestParam("targetId") int targetId, @RequestParam("type") int type, HttpSession session) { User user = (User) session.getAttribute("user"); if (user == null) { return Result.fail("请先登录!"); } LikeInfo likeInfo = likeService.getLikeInfo(user.getId(), targetId); if (likeInfo != null) { return Result.fail("您已经点过赞了!"); } likeInfo = new LikeInfo(); likeInfo.setUserId(user.getId()); likeInfo.setTargetId(targetId); likeInfo.setType(type); likeService.addLikeInfo(likeInfo); return Result.success(); } } ``` 在Controller中,首先判断用户是否已经登录,如果没有登录则返回错误信息。然后根据用户id和目标id查询数据库,判断用户是否已经点过赞,如果已经点过赞则返回错误信息。最后如果用户没有点过赞,则添加一条点赞记录到数据库中,并返回成功信息。 (3)更新点赞状态和点赞数 当服务器返回成功信息时,可以通过JavaScript来更新页面上的点赞状态和点赞数。例如: ```javascript var likeButton = $("#likeButton"); if (likeButton.hasClass("liked")) { likeButton.removeClass("liked"); likeButton.attr("title", "点赞"); var likeCount = parseInt($("#likeCount").text()) - 1; $("#likeCount").text(likeCount); } else { likeButton.addClass("liked"); likeButton.attr("title", "取消赞"); var likeCount = parseInt($("#likeCount").text()) + 1; $("#likeCount").text(likeCount); } ``` 在页面上,点赞按钮可以使用CSS样式来实现点赞状态的切换。例如: ```css .like-button { display: inline-block; width: 20px; height: 20px; background: url("/images/like.png") no-repeat center center; background-size: 20px 20px; cursor: pointer; } .like-button.liked { background: url("/images/liked.png") no-repeat center center; background-size: 20px 20px; } ``` 当用户点击点赞按钮时,如果按钮已经有liked类,则表示用户已经点过赞,此时需要将liked类移除,并且将点赞数减1。如果按钮没有liked类,则表示用户还没有点赞,此时需要添加liked类,并且将点赞数加1。同时,还需要将按钮的title属性修改为“点赞”或“取消赞”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值