python 全棧開發,Day83(部落格系統子評論,後臺管理,富文字編輯器kindeditor,bs4模組)
一、子評論
必須點選回覆,才是子評論!否則是根評論
點選回覆之後,定位到輸入框,同時加入@評論者的使用者名稱
定位輸入框
focus
focus:獲取物件焦點觸發事件
先做樣式。點選回覆之後,定位到輸入框,加入被評論的使用者名稱
給回覆的a標籤加一個class=reply_btn,關閉a標籤的跳轉,使用javascript:void(0)
修改article_detail.html,增加一段回覆的js
{% extends "base.html" %} {% block content %} <div class="article_infoView Code"> <h4 class="text-center">{{ article_obj.title }}</h4> <div class="content"> {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips"> </div> </div> <div class="clearfix"></div> <div class="comment"> <p>評論列表</p> <ul class="comment_list list-group"> {% for comment in comment_list %} <li class="list-group-item"> <div> <a href="">#{{ forloop.counter }}樓</a> <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href="">{{ comment.user.username }}</a> <a href="javascript:void(0)" class="pull-right reply_btn"><span>回覆</span></a> </div> <div> <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul> <p>發表評論</p> <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <div> <textarea name="" id="comment_content" cols="60" rows="10"></textarea> </div> <input type="button" value="submit" class="btn btn-default comment_btn"> </div> </div> {% csrf_token %} <script> // 點贊和踩滅 $(".action").click(function () { {#hasClass() 方法檢查被選元素是否包含指定的 class#} var is_up = $(this).hasClass("diggit"); {#獲取提示的span標籤#} var _this = $(this).children("span"); {#判斷是否登入#} if ("{{ request.user.username }}") { $.ajax({ url: "/digg/", type: "post", data: { is_up: is_up, article_id: "{{ article_obj.pk }}", csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() }, success: function (data) { console.log(data); console.log(typeof data); if (data.state) { //提交成功 var val = _this.text(); //獲取text值 //在原有的基礎上加1。注意:一定要進行型別轉換 _this.text(parseInt(val) + 1) } else { // 重複提交 var val = data.handled ? "您已經推薦過!" : "您已經反對過!"; $("#digg_tips").html(val); setTimeout(function () { $("#digg_tips").html("") //清空提示文字 }, 1000) } } }) } else { location.href = "/login/"; } }) // 提交評論 $(".comment_btn").click(function () { {#評論內容#} var content = $("#comment_content").val(); {#預設為空#} var pid = ""; $.ajax({ url: "/comment/", type: "post", data: { content: content, article_id: "{{ article_obj.pk }}", pid: pid, csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() }, success: function (data) { console.log(data); {#獲取3個值#} var comment_time = data.timer; var comment_content = data.content; var comment_user = data.user; {#組織li標籤#} var $li = ` <li class="list-group-item"> <div> <span class="small">${comment_time}</span> <a href="">${comment_user}</a> </div> <div> <p>${comment_content}</p> </div> </li>`; {#追加到評論列表中#} $(".comment_list").append($li); // 清空輸入框的內容 $("#comment_content").val("") } }) }) // 回覆按鈕事件 $(".reply_btn").click(function () { {#獲取到textarea輸入框游標#} $("#comment_content").focus(); }) </script> {% endblock %}
換一個zhang使用者登入,訪問一篇有回覆的部落格,點選回覆,效果如下:
它會自動定位到輸入框的位置
增加@評論使用者
注意:這裡的評論使用者是這一條評論的使用者,比如上面的xiao
那麼js如何獲取評論人的使用者名稱呢?使用render渲染就可以了!
給回覆的a標籤新增一個自定義屬性username
$this能直接獲取這個a標籤。
修改article_detail.html
{% extends "base.html" %} {% block content %} <div class="article_info"> <h4 class="text-center">{{ article_obj.title }}</h4> <div class="content"> {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips"> </div> </div> <div class="clearfix"></div> <div class="comment"> <p>評論列表</p> <ul class="comment_list list-group"> {% for comment in comment_list %} <li class="list-group-item"> <div> <a href="">#{{ forloop.counter }}樓</a> <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href="">{{ comment.user.username }}</a> <a href="javascript:void(0)" class="pull-right reply_btn" username="{{ comment.user.username }}"><span>回覆</span></a> </div> <div> <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul> <p>發表評論</p> <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <div> <textarea name="" id="comment_content" cols="60" rows="10"></textarea> </div> <input type="button" value="submit" class="btn btn-default comment_btn"> </div> </div> {% csrf_token %} <script> // 點贊和踩滅 $(".action").click(function () { {#hasClass() 方法檢查被選元素是否包含指定的 class#} var is_up = $(this).hasClass("diggit"); {#獲取提示的span標籤#} var _this = $(this).children("span"); {#判斷是否登入#} if ("{{ request.user.username }}") { $.ajax({ url: "/digg/", type: "post", data: { is_up: is_up, article_id: "{{ article_obj.pk }}", csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() }, success: function (data) { console.log(data); console.log(typeof data); if (data.state) { //提交成功 var val = _this.text(); //獲取text值 //在原有的基礎上加1。注意:一定要進行型別轉換 _this.text(parseInt(val) + 1) } else { // 重複提交 var val = data.handled ? "您已經推薦過!" : "您已經反對過!"; $("#digg_tips").html(val); setTimeout(function () { $("#digg_tips").html("") //清空提示文字 }, 1000) } } }) } else { location.href = "/login/"; } }) // 提交評論 $(".comment_btn").click(function () { {#評論內容#} var content = $("#comment_content").val(); {#預設為空#} var pid = ""; $.ajax({ url: "/comment/", type: "post", data: { content: content, article_id: "{{ article_obj.pk }}", pid: pid, csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() }, success: function (data) { console.log(data); {#獲取3個值#} var comment_time = data.timer; var comment_content = data.content; var comment_user = data.user; {#組織li標籤#} var $li = ` <li class="list-group-item"> <div> <span class="small">${comment_time}</span> <a href="">${comment_user}</a> </div> <div> <p>${comment_content}</p> </div> </li>`; {#追加到評論列表中#} $(".comment_list").append($li); // 清空輸入框的內容 $("#comment_content").val("") } }) }) // 回覆按鈕事件 $(".reply_btn").click(function () { {#獲取到textarea輸入框游標#} $("#comment_content").focus(); {#增加@+使用者名稱+換行符#} var val = "@" + $(this).attr("username") + "\n"; {#修改輸入框的值#} $("#comment_content").val(val); }) </script> {% endblock %}View Code
重新重新整理頁面,並點選回覆,效果如下:
首頁增加連線
修改index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"> <script src="/static/js/jquery.js"></script> <script src="/static/bootstrap/js/bootstrap.js"></script> <style> .desc { text-align: justify; } .info { margin-top: 10px; } h5 a { color: #105cb6; font-size: 14px; font-weight: bold; text-decoration: underline; } .diggit { float: left; margin-right: 20px; width: 46px; height: 52px; background-image: url('/static/img/upup.gif'); } .diggnum { position: relative; top: 6px; left: 20px; } </style> </head> <body> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <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="#">部落格園</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">新聞 <span class="sr-only">(current)</span></a></li> <li><a href="#">博問</a></li> </ul> <ul class="nav navbar-nav navbar-right"> {% if request.user.username %} <li><a href="#"><span class="glyphicon glyphicon-user"></span> {{ request.user.username }}</a> </li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">修改密碼</a></li> <li><a href="#">個人資訊</a></li> <li><a href="/logout/">登出</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> {% else %} <li><a href="/login/">登陸</a></li> <li><a href="#">註冊</a></li> {% endif %} </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-danger"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-6"> <div class="article_list"> {% for article in article_list %} <div class="diggit"> <span class="diggnum">0</span> </div> <div class="article_item"> <h5><a href="/{{ article.user.username }}/articles/{{ article.pk }}">{{ article.title }}</a></h5> <div> <span class="media-left"><a href="/{{ article.user.username }}"><img width="48" height="48" src="https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1758343206,1224786249&fm=58&bpow=1024&bpoh=1536" alt=""></a></span> <span class="media-right small desc "> {{ article.desc }} </span> </div> <div class="info small"> <span><a href="/{{ article.user.username }}">{{ article.user.username }}</a></span> 釋出於 <span>{{ article.create_time|date:'Y-m-d H:i' }}</span> <img src="/static/img/icon_comment.gif" alt=""><a href="/{{ article.user.username }}/articles/{{ article.pk }}">評論({{ article.comment_count }})</a> <span class="glyphicon glyphicon-thumbs-up"></span><a href="/{{ article.user.username }}/articles/{{ article.pk }}">點贊({{ article.up_count }})</a> </div> </div> <hr> {% endfor %} </div> </div> <div class="col-md-3"> <div class="panel panel-warning"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-info"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> </div> </div> </div> </body> </html>View Code
效果如下:
點選文章標題,可以進入文章詳情頁。
提交子評論
如果是下面這種狀態,直接回復後,就是一個根評論
為什麼呢?因為前臺和後端,沒有做判斷!
ajax提交資料時,pid的變數不應該為空,應該傳送一個父評論id才行!
什麼時候,對pid賦值呢?
正確做法是:點選回覆時,將pid賦值。
怎麼拿到父評論的id呢?使用render渲染,給a標籤加一個子定義屬性comment_id
然後利用js獲取,進行賦值即可
獲取父評論id,並賦值
修改article_detail.html
{% extends "base.html" %} {% block content %} <div class="article_info"> <h4 class="text-center">{{ article_obj.title }}</h4> <div class="content"> {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips"> </div> </div> <div class="clearfix"></div> <div class="comment"> <p>評論列表</p> <ul class="comment_list list-group"> {% for comment in comment_list %} <li class="list-group-item"> <div> <a href="">#{{ forloop.counter }}樓</a> <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href="">{{ comment.user.username }}</a> <a href="javascript:void(0)" class="pull-right reply_btn" username="{{ comment.user.username }}" comment_id="{{ comment.pk }}"><span>回覆</span></a> </div> <div> <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul> <p>發表評論</p> <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <div> <textarea name="" id="comment_content" cols="60" rows="10"></textarea> </div> <input type="button" value="submit" class="btn btn-default comment_btn"> </div> </div> {% csrf_token %} <script> // 點贊和踩滅 $(".action").click(function () { {#hasClass() 方法檢查被選元素是否包含指定的 class#} var is_up = $(this).hasClass("diggit"); {#獲取提示的span標籤#} var _this = $(this).children("span"); {#判斷是否登入#} if ("{{ request.user.username }}") { $.ajax({ url: "/digg/", type: "post", data: { is_up: is_up, article_id: "{{ article_obj.pk }}", csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() }, success: function (data) { console.log(data); console.log(typeof data); if (data.state) { //提交成功 var val = _this.text(); //獲取text值 //在原有的基礎上加1。注意:一定要進行型別轉換 _this.text(parseInt(val) + 1) } else { // 重複提交 var val = data.handled ? "您已經推薦過!" : "您已經反對過!"; $("#digg_tips").html(val); setTimeout(function () { $("#digg_tips").html("") //清空提示文字 }, 1000) } } }) } else { location.href = "/login/"; } }) // 提交評論 $(".comment_btn").click(function () { {#評論內容#} var content = $("#comment_content").val(); {#預設為空#} var pid = ""; $.ajax({ url: "/comment/", type: "post", data: { content: content, article_id: "{{ article_obj.pk }}", pid: pid, csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() }, success: function (data) { console.log(data); {#獲取3個值#} var comment_time = data.timer; var comment_content = data.content; var comment_user = data.user; {#組織li標籤#} var $li = ` <li class="list-group-item"> <div> <span class="small">${comment_time}</span> <a href="">${comment_user}</a> </div> <div> <p>${comment_content}</p> </div> </li>`; {#追加到評論列表中#} $(".comment_list").append($li); // 清空輸入框的內容 $("#comment_content").val("") } }) }) // 回覆按鈕事件 $(".reply_btn").click(function () { {#獲取到textarea輸入框游標#} $("#comment_content").focus(); {#增加@+使用者名稱+換行符#} var val = "@" + $(this).attr("username") + "\n"; {#修改輸入框的值#} $("#comment_content").val(val); {#pid賦值#} pid=$(this).attr("comment_id"); console.log(pid); }) </script> {% endblock %}View Code
重新整理網頁,發現自定義屬性comment_id以及被渲染出來了
點選回覆,pid就能打印出來
正式提交子評論
修改article_detail.html,修改pid變數的位置
{% extends "base.html" %} {% block content %} <div class="article_info"> <h4 class="text-center">{{ article_obj.title }}</h4> <div class="content"> {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips"> </div> </div> <div class="clearfix"></div> <div class="comment"> <p>評論列表</p> <ul class="comment_list list-group"> {% for comment in comment_list %} <li class="list-group-item"> <div> <a href="">#{{ forloop.counter }}樓</a> <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href="">{{ comment.user.username }}</a> <a href="javascript:void(0)" class="pull-right reply_btn" username="{{ comment.user.username }}" comment_id="{{ comment.pk }}"><span>回覆</span></a> </div> <div> <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul> <p>發表評論</p> <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <div> <textarea name="" id="comment_content" cols="60" rows="10"></textarea> </div> <input type="button" value="submit" class="btn btn-default comment_btn"> </div> </div> {% csrf_token %} <script> // 點贊和踩滅 $(".action").click(function () { {#hasClass() 方法檢查被選元素是否包含指定的 class#} var is_up = $(this).hasClass("diggit"); {#獲取提示的span標籤#} var _this = $(this).children("span"); {#判斷是否登入#} if ("{{ request.user.username }}") { $.ajax({ url: "/digg/", type: "post", data: { is_up: is_up, article_id: "{{ article_obj.pk }}", csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() }, success: function (data) { console.log(data); console.log(typeof data); if (data.state) { //提交成功 var val = _this.text(); //獲取text值 //在原有的基礎上加1。注意:一定要進行型別轉換 _this.text(parseInt(val) + 1) } else { // 重複提交 var val = data.handled ? "您已經推薦過!" : "您已經反對過!"; $("#digg_tips").html(val); setTimeout(function () { $("#digg_tips").html("") //清空提示文字 }, 1000) } } }) } else { location.href = "/login/"; } }) // 提交評論 var pid=""; //預設為空,表示父評論 $(".comment_btn").click(function () { {#評論內容#} var content = $("#comment_content").val(); $.ajax({ url: "/comment/", type: "post", data: { content: content, article_id: "{{ article_obj.pk }}", pid: pid, csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() }, success: function (data) { console.log(data); {#獲取3個值#} var comment_time = data.timer; var comment_content = data.content; var comment_user = data.user; {#組織li標籤#} var $li = ` <li class="list-group-item"> <div> <span class="small">${comment_time}</span> <a href="">${comment_user}</a> </div> <div> <p>${comment_content}</p> </div> </li>`; {#追加到評論列表中#} $(".comment_list").append($li); // 清空輸入框的內容 $("#comment_content").val("") } }) }) // 回覆按鈕事件 $(".reply_btn").click(function () { {#獲取到textarea輸入框游標#} $("#comment_content").focus(); {#增加@+使用者名稱+換行符#} var val = "@" + $(this).attr("username") + "\n"; {#修改輸入框的值#} $("#comment_content").val(val); {#pid賦值#} pid=$(this).attr("comment_id"); console.log(pid); }) </script> {% endblock %}View Code
提交一條子評論
效果如下:
檢視blog_comment表記錄,多了一條記錄
nid對應的是parent_comment_id
但是評論內容有些不妥,它加了@xiao
去掉@使用者名稱
怎麼去掉@使用者名稱呢?使用正則匹配?太麻煩了!
使用切片?比如s.slice(1),貌似不好做!
觀察一下規律,在輸入框中。第一行,永遠是@使用者名稱。第二行,才是使用者真正的評論內容。
indexOf()
indexOf() 方法可返回某個指定的字串值在字串中首次出現的位置。
如果沒有找到匹配的字串則返回 -1
思路:先找到換行符,使用indexOf。再加1,表示下一行,就可以得到真正的評論內容
先來測試一下,在templates目錄中,新建一個test.html,內容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> var aa = "@xiao\n34324234"; var index = aa.indexOf("\n"); content = aa.slice(index+1); console.log(content); </script> </body> </html>View Code
直接用谷歌瀏覽器開啟,注意:這已經脫離了django,是直接訪問的。
可以直接得到評論內容,說明是可行的!
修改article_detail.html
{% extends "base.html" %} {% block content %} <div class="article_info"> <h4 class="text-center">{{ article_obj.title }}</h4> <div class="content"> {{ 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="burynum" id="bury_count">{{ article_obj.down_count }}</span> </div> <div class=<