二十三.新闻详情页 2021-04-26

二十三.新闻详情页

注:该篇文章接上一篇 二十二.新闻主页
在上一篇文章我们实现了资讯模块中的新闻主页功能,在这一章实现点击新闻进入新闻详细页面.

一、功能需求分析

1.功能

  1. 新闻详情
  2. 加载评论功能
  3. 添加评论功能

二.新闻详细内容页面

通过新闻的id来从数据库取得新闻的详细内容,新闻的插图,新闻的作者的信息

1.业务流程分析

业务流程:

  1. 判断前端传递新闻id是否为空,是否为整数,是否存在

2.接口设计

  1. 接口说明:
类目说明
请求方法GET
url定义/news/int:news_id/
参数格式url路径参数
  1. 参数说明:
参数名类型是否必须描述
news_id整数新闻id
  1. 返回结果:
    html页面,直接通过模板渲染的方式实现

3.后端页面

  1. 在news/views.py中添加信息详细显示代码
class NewDetailView(View):
    """
    显示新闻页面的详细新闻信息
    """
    def get(self,request,news_id):
        # 通过get请求的以及每个新闻的id来获取详细的新闻信息
        #select_related 直接把要从数据库中得到的值传到News中,下次再进行查询时可以直接在News中查询,不必进入数据库中
        news = News.objects.select_related('tag','author').only('title','content','update_time','tag__name','author__username').filter(is_delete=False,id =  news_id).first()

        # 判断news是否存在
        if news:
            return render(request,'news/news_detail.html',context={'news':news})
        else:
            return HttpResponesNotFound('<h1>Page not found</h1>')            # 报错404 自行定义的响应错误代码,记得导入

  1. 在news/urls.py文件中设置路由
from django.urls import path
from . import views
# url的命名空间
app_name = 'news'

urlpatterns = [
    path('', views.index, name='index'),    # 将这条路由命名为index
    # 新闻列表
    path('news/',views.NewsListViews.as_view(),name = 'news_list'),
    #展示轮播图
    path('news/banners/',views.NewBannerViews.as_view(),name = 'news_banners'),
    # 访问新闻详情页面
    path('news/<int:news_id>/',views.NewDetailView.as_view(),name = 'news_detail')
]

4.前端页面

  1. 在templates/news/new_detail.html文件下代码如下
    在这里插入图片描述
{% extends 'base/base.html' %}
{% load static %}
{% block title %}文章详情{% endblock title %}
{% block link %}
  <link rel="stylesheet" href="{% static '/css/news/news-detail.css' %}">
{% endblock link %}

<!-- main start -->

<main id="main">
  <div class="w1200 clearfix">
      {% block main_contain %}
    <!-- news-contain start  -->
    <div class="news-contain">
      <h1 class="news-title">{{ news.title }}</h1>
      <div class="news-info">
        <div class="news-info-left">
          <span class="news-author">{{ new.author.username }}</span>
          <span class="news-pub-time">{{ news.update_time }}</span>
          <span class="news-type">{{ news_tag.name }}</span>
        </div>
      </div>
      <article class="news-content">
        {{ news.content|safe }}
      </article>
      <div class="comment-contain">
        <div class="comment-pub clearfix">
          <div class="new-comment">
            文章评论(<span class="comment-count">0</span>)
          </div>
          <div class="comment-control please-login-comment" style="display:none;">
            <input type="text" placeholder="请登录后参加评论">
          </div>
          <div class="comment-control logged-comment">
            <input type="text" placeholder="请填写评论">
          </div>
          <button class="comment-btn">发表评论</button>
        </div>
        <ul class="comment-list">
          <li class="comment-item">
            <div class="comment-info clearfix">
              <img src="../images/avatar.jpeg" alt="avatar" class="comment-avatar">
              <span class="comment-user">评论人</span>
              <span class="comment-pub-time">1小时前</span>
            </div>
            <div class="comment-content">这是一条评论</div>
          </li>
          <li class="comment-item">
            <div class="comment-info clearfix">
              <img src="../images/avatar.jpeg" alt="avatar" class="comment-avatar">
              <span class="comment-user">评论人</span>
              <span class="comment-pub-time">1小时前</span>
            </div>
            <div class="comment-content">这是一条评论</div>
          </li>
        </ul>
      </div>

    </div>
      {% endblock %}

  </div>
</main>
<!-- main end -->
{% block script%}
   <script src="{% static 'js/news/news_detail.js' %}"></script>
{% endblock script %}
  1. 在static/css/news/new-detail.css中添加代码:
.news-content p{
  font-size: 16px;
  line-height: 26px;
  text-align: justify;
  word-wrap: break-word;
  padding: 3px 0 ;
}

运行结果:

  • 因为没有实现js代码无法实现点击跳转
  • 在给定路由可以进入news的详情页面
  • 由于暂时没有设置评论人,所以目前没有信息

在这里插入图片描述

  1. 在static/js/news中添加news_detail.js文件

三、加载新闻评论

1.接口设计

新闻详情页,直接渲染新闻评论

2.后端代码

模型代码

# 本项目设计二级评论,修改Comments模型,添加一个parent字段
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True)

修改模型后一定要及时迁移
在这里插入图片描述
导入数据

链接:https://pan.baidu.com/s/11-TMVONmDRCfygze3Sq-pQ
提取码:spmd
在这里插入图片描述

# 导入测试数据tb_comments_20181222.sql
# 一定要保证tb_users中有id为1,2,3的三个用户,不然导入测试数据会报错
mysql -u用户名 -p -D 数据库名< tb_comments_20181222.sql

视图代码

class NewDetailView(View):
    """
    显示新闻页面的详细新闻信息
    """
    def get(self,request, news_id):
        # 通过get请求的以及每个新闻的id来获取详细的新闻信息
        #select_related 直接把要从数据库中得到的值传到News中,下次再进行查询时可以直接在News中查询,不必进入数据库中
        news = News.objects.select_related('tag','author').only('title','content','update_time','tag__name','author__username').filter(is_delete=False,id = news_id).first()

        # 判断news是否存在
        if news:
            #获取父级评论
             comments = Commments.objects.select_related('author', 'parent').only(
                'comment', 'author__username', 'update_time', 'parent__author__username',
                'parent__comment','parent__update_time').filter(is_delete=False, id = news_id)

            return render(request,'news/news_detail.html',context={'news':news,'comments':comments})
        else:
            return HttpResponesNotFound('<h1>Page not found</h1>')            # 报错404 自行定义的响应错误代码,记得导入

3. 前端代码

  1. html代码
<!-- 在templates/news/news_detail.html文件中class="comment-contain"里面加入如下代码: -->
<div class="comment-contain">
            <div class="comment-pub clearfix">
                <div class="new-comment">
                    文章评论(<span class="comment-count">0</span>)
                </div>
                <div class="comment-control please-login-comment" style="display:none;">
                    <input type="text" placeholder="请登录后参加评论">
                </div>
                <div class="comment-control logged-comment">
                    <input type="text" placeholder="请填写评论">
                </div>
                <button class="comment-btn">发表评论</button>
            </div>
            <ul class="comment-list">
                {% for comment in comments %}
                    <li class="comment-item">
                        <div class="comment-info clearfix">
                            <img src="/static/images/avatar.jpeg" alt="avatar" class="comment-avatar">
                            <span class="comment-user">{{ comment.author.username }}</span>
                            <span class="comment-pub-time">{{ comment.update_time }}</span>
                        </div>
                        <div class="comment-content">{{ comment.comment }}</div>

                        {% if comment.parent %}
                            <div class="parent_comment_text">
                                <div class="parent_username">{{ comment.parent.author }}</div>
                                <div class="comment_time">{{ comment.parent.update_time }}</div>

                                <div class="parent_content_text">
                                    {{ comment.parent.comment }}
                                </div>

                            </div>
                        {% endif %}

                        <a href="javascript:void(0);" class="reply_a_tag right_float">回复</a>
                        <form class="reply_form left_float" comment-id="{{ comment.id }}"
                              news-id="{{ comment.news_id }}">
                            <textarea class="reply_input"></textarea>
                            <input type="button" value="回复" class="reply_btn right_float">
                            <input type="reset" name="" value="取消" class="reply_cancel right_float">
                        </form>

                    </li>
                {% endfor comments %}

            </ul>
        </div>
  1. js代码
// 在static/js/news/news_detail.js中加入如下代码:

$(function () {
  $('.comment-list').delegate('a,input', 'click', function () {
    //获取回复按钮的class属性
    let sClassValue = $(this).prop('class');
    // 如果点击的是回复按钮,就显示输入框
    if (sClassValue.indexOf('reply_a_tag') >= 0) {
      $(this).next().toggle();
    }
    // 如果点击的是取消按钮,就隐藏输入框
    if (sClassValue.indexOf('reply_cancel') >= 0) {
      $(this).parent().toggle();
    }

    if (sClassValue.indexOf('reply_btn') >= 0) {
      // 评论
    }
  });
 
});

在这里插入图片描述

  1. 更改css改变回复框
/* 在static/css/news/news-detail.css中添加如下代码: */
.comment-list .comment-item {
  /*把这条样式注释掉*/
  /*border-bottom: 1px solid #ddd;*/
  margin-bottom: 30px;
}
/* ========= 为父评论添加样式 start============ */
.left_float{
	float:left;
}

.right_float{
	float:right;
}

.parent_comment_text{
    width:698px;
    padding:8px;
    background: #f4facf;
    margin:10px 0 0 60px;
}

.comment_time{
    font-size:12px;
    color:#999;
    margin:10px 0 0 60px;
}

.parent_comment_text .parent_username{
    font-size:12px;
    color:#000;
    display:inline-block;
}
.parent_comment_text .comment_time{
   display: inline-block;
   float:right;
}

.parent_comment_text .parent_content_text{
    color:#666;
    font-size:14px;
    margin-top: 20px;
}

.reply_a_tag{
    font-size:12px;
    color:#999;
    text-indent:20px;
    margin:10px 0 0 20px;
    background:url('/static/images/content_icon.png') left center no-repeat;
}

.reply_form{
    width:718px;
    overflow:hidden;
    margin:10px 0 0 60px;
    display:none;
}

.reply_input{
    float:left;
    width:692px;
    height:30px;
    border-radius:4px;
    padding:10px;
    outline:none;
    border:1px solid #2185ed;
}

.reply_btn,.reply_cancel{
    width:40px;
    height:23px;
    background:#76b6f4;
    border:0px;
    border-radius:2px;
    color:#fff;
    margin:10px 5px 0 10px;
    cursor:pointer;
}

.reply_cancel{
    background:#fff;
    color: #909090;
}
/* ========= 为父评论添加样式 end============ */
将content_icon.png图片放到static/images/中

在这里插入图片描述
4. 在news下的 index.html中修改如下代码 可以实现点击新闻列表的图片进入到新闻详情

在这里插入代码片

4.2 .在static/news/index.js文件中改写代码
目的:实现点击新闻跳转到详细页面
目前状况如下,只能够跳转到最上面的新闻页面
在这里插入图片描述

分析:经过分析需要改变a标签的链接
在这里插入图片描述

1.1 进入到static/news/index.js下面,将a标签的href 改为

<a href="/news/${one_news.id}/" class="news-thumbnail"
                         target="_blank">

在这里插入图片描述

四、添加新闻评论功能

1.业务流程分析

业务处理流程:

  1. 判断用户是否登录
  2. 判断前端传的新闻id是否为空,是否为整数,是否存在
  3. 判断评论内容是否为空
  4. 判断是否有父评论,父评论id是否与新闻id匹配
  5. 保存新闻评论

2.接口设计

  1. 接口说明:
类目说明
请求方法POST
url定义/news/int:news_id/comment/
参数格式url路径参数,表单参数
  1. 参数说明:
参数名类型是否必须描述
news_id整数新闻id
content字符串新闻评论内容
parent_id整数父评论id

注意:post请求需要携带csrftoken

  1. 返回结果:
    {
    	"errno": "0",
    	"errmsg": "",
    	"data": {
    		"news_id": 1170,
    		"content_id": 3569,
    		"content": "评论比较中肯。",
    		"author": "admin",
    		"update_time": "2019年08月19日 16:00",
    		"parent": {
    			"news_id": 1170,
    			"content_id": 893,
    			"content": "行文思路简单肤浅,文章结构平面呆板。",
    			"author": "xinlan",
    			"update_time": "2018年12月21日 11:17",
    			"parent": null
    		}
    	}
    }

3.后端代码

视图代码

# 在news/views.py中编写如下视图
class NewsCommentView(View):
    """
    添加评论视图
    url: /news/<int:news_id>/comment/
    """
    def post(self, request, news_id):
        # 是否登录
        if not request.user.is_authenticated:   # is_authenticated 判断是否登录,通过base.html条件
            return json_response(errno=Code.SESSIONERR, errmsg=error_map[Code.SESSIONERR])    #返回错误信息
        # 新闻是否存在
        if not News.objects.only('id').filter(is_delete=False, id=news_id).exists():
            return json_response(errno=Code.PARAMERR, errmsg='新闻不存在!')

        content = request.POST.get('content')
        # 内容是否为空
        if not content:
            return json_response(errno=Code.PARAMERR, errmsg='评论内容不能为空!')

        # 二级评论是否存在
        parent_id = request.POST.get('parent_id')
        if parent_id:
            try:
                parent_id = int(parent_id)
                #先查id再按照父级评论和新闻id查看是否存在二级评论
                if not Commments.objects.only('id').filter(is_delete=False, id=parent_id, news_id=news_id).exists():
                    return json_response(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
            except Exception as e:
                logger.info('前端传递过来的parent_id异常\n{}'.format(e))
                return json_response(errno=Code.PARAMERR, errmsg='未知异常')

        # 保存到数据库
        new_comment = Commments()      # 先创建一个空对象
        new_comment.content = content
        new_comment.news_id = news_id
        new_comment.author = request.user
        new_comment.parent_id = parent_id if parent_id else None   #判断perent_id是否为空
        new_comment.save()

        return json_response(data=new_comment.to_dict_data())

序列化comment对象

# 在news/models.py的Comment模型中添加如下方法,用来序列化
def to_dict_data(self):
    comment_dict = {
        'news_id': self.news_id,
        'content_id': self.id,
        'content': self.content,
        'author': self.author.username,
        'update_time': self.update_time.astimezone().strftime('%Y年%m月%d日 %H:%M'),
        'parent': self.parent.to_dict_data() if self.parent else None
    }
    return comment_dict

路由

# 在news/urls.py中添加如下路由
path('news/<int:news_id>/comment/', views.NewsCommentView.as_view(), name='news_comment'),

4.前端代码

html

<!-- 修改templates/news/news_detail.html中评论部分代码如下 -->
{% extends 'base/base.html' %}
{% load static %}
{% block title %}文章详情{% endblock title %}
{% block link %}
  <link rel="stylesheet" href="{% static '/css/news/news-detail.css' %}">
{% endblock link %}

<!-- main start -->

<main id="main">
  <div class="w1200 clearfix">
      {% block main_contain %}
    <!-- news-contain start  -->
    <div class="news-contain">
      <h1 class="news-title">{{ news.title }}</h1>
      <div class="news-info">
        <div class="news-info-left">
          <span class="news-author">{{ new.author.username }}</span>
          <span class="news-pub-time">{{ news.update_time }}</span>
          <span class="news-type">{{ news_tag.name }}</span>
        </div>
      </div>
      <article class="news-content">
        {{ news.content|safe }}
      </article>
      <div class="comment-contain">
            <div class="comment-pub clearfix">
                <div class="new-comment">
                    文章评论(<span class="comment-count">0</span>)
                </div>
                {% if user.is_authenticated %}
                    <div class="comment-control logged-comment">
                        <input type="text" placeholder="请填写评论">
                    </div>
                {% else %}
                <div class="comment-control please-login-comment" >
                    <input type="text" placeholder="请登录后参加评论">
                </div>
                {% endif %}
                <button class="comment-btn">发表评论</button>
                {% csrf_token %}

            </div>
            <ul class="comment-list">
                {% for comment in comments %}
                    <li class="comment-item">
                        <div class="comment-info clearfix">
                            <img src="/static/images/avatar.jpeg" alt="avatar" class="comment-avatar">
                            <span class="comment-user">{{ comment.author.username }}</span>
                            <span class="comment-pub-time">{{ comment.update_time }}</span>
                        </div>
                        <div class="comment-content">{{ comment.comment }}</div>

                        {% if comment.parent %}
                            <div class="parent_comment_text">
                                <div class="parent_username">{{ comment.parent.author }}</div>
                                <div class="comment_time">{{ comment.parent.update_time }}</div>

                                <div class="parent_content_text">
                                    {{ comment.parent.comment }}
                                </div>

                            </div>
                        {% endif %}

                        <a href="javascript:void(0);" class="reply_a_tag right_float">回复</a>
                        <form class="reply_form left_float" comment-id="{{ comment.id }}"
                              news-id="{{ comment.news_id }}">
                            <textarea class="reply_input"></textarea>
                            <input type="button" value="回复" class="reply_btn right_float">
                            <input type="reset" name="" value="取消" class="reply_cancel right_float">
                        </form>

                    </li>
                {% endfor comments %}

            </ul>
        </div>

    </div>
      {% endblock %}
  </div>
</main>
<!-- main end -->
{% block script%}
    <script src="{% static 'js/news/news_detail.js' %}"></script>
{% endblock script %}

js


$(function () {
    // 对评论进行评论
    $('.comment-list').delegate('a,input', 'click', function () {
        //获取回复按钮的class属性
        let sClassValue = $(this).prop('class');
        // 如果点击的是回复按钮,就显示输入框
        if (sClassValue.indexOf('reply_a_tag') >= 0) {
            $(this).next().toggle();
        }
        // 如果点击的是取消按钮,就隐藏输入框
        if (sClassValue.indexOf('reply_cancel') >= 0) {
            $(this).parent().toggle();
        }

        if (sClassValue.indexOf('reply_btn') >= 0) {
            // 评论
            let $this = $(this);
            let news_id = $this.parent().attr('news-id');
            let parent_id = $this.parent().attr('comment-id');
            let content = $this.prev().val();
            if (!content) {
                message.showError('请输入评论内容!');
                return
            }
            $
                .ajax({
                    url: '/news/' + news_id + '/comment/',
                    type: 'POST',
                    data: {
                        content: content,
                        parent_id: parent_id
                    },
                    dataType: "json"
                })

                .done((res) => {
                    if (res.errno === '0') {
                        let comment = res.data;
                        let html_comment = `<li class="comment-item">
            <div class="comment-info clearfix">
              <img src="/static/images/avatar.jpeg" alt="avatar" class="comment-avatar">
              <span class="comment-user">${comment.author}</span>
            </div>
            <div class="comment-content">${comment.content}</div>

                <div class="parent_comment_text">
                  <div class="parent_username">${comment.parent.author}</div>
                  <div class="comment_time">${comment.parent.update_time}</div>
                  <div class="parent_content_text">
                    ${comment.parent.content}
                  </div>
                </div>

              <div class="comment_time left_float">${comment.update_time}</div>
              <a href="javascript:;" class="reply_a_tag right_float">回复</a>
              <form class="reply_form left_float" comment-id="${comment.content_id}" news-id="${comment.news_id}">
                <textarea class="reply_input"></textarea>
                <input type="button" value="回复" class="reply_btn right_float">
                <input type="reset" name="" value="取消" class="reply_cancel right_float">
              </form>

          </li>`;
                        message.showSuccess('评论成功!');
                        setTimeout(() => {
                            $('.comment-list').prepend(html_comment);
                        }, 800);

                        $this.prev().val('');   // 清空输入框
                        $this.parent().hide();  // 关闭评论框
                    } else if (res.errno === '4101') {
                        // 用户未登录
                        message.showError(res.errmsg);
                        setTimeout(() => {
                            window.location.href = '/login/'
                        }, 800)
                    } else {
                        // 失败
                        message.showError(res.errmsg)
                    }
                })
                .fail(() => {
                    message.showError('服务器超时,请重试')
                })
        }
    });
    // 对新闻评论
    let $newsComment = $('.logged-comment input');            // 新闻评论框
    let $sendComment = $('.comment-pub .comment-btn');           // 新闻评论按钮

    $sendComment.click(function () {

        let $this = $(this);
        if ($this.prev().hasClass('please-login-comment')) {
            message.showError('未登录,请登录后再评论!');
            setTimeout(() => {
                window.location.href = '/login/'
            }, 800);
            return
        }
        let news_id = $this.prev().attr('news-id');
        let content = $newsComment.val();
        if (!content) {
            message.showError('请输入评论内容!');
            return
        }
        $
            .ajax({
                url: '/news/' + news_id + '/comment/',
                type: 'POST',
                data: {
                    content: content
                },
                dataType: 'json'
            })
            .done((res) => {
                if (res.errno === '0') {
                    let comment = res.data;
                    let html_comment = `<li class="comment-item">
            <div class="comment-info clearfix">
              <img src="/static/images/avatar.jpeg" alt="avatar" class="comment-avatar">
              <span class="comment-user">${comment.author}</span>
              <span class="comment-pub-time">${ comment.update_time }</span>
            </div>
            <div class="comment-content">${comment.content}</div>

              <a href="javascript:;" class="reply_a_tag right_float">回复</a>
              <form class="reply_form left_float" comment-id="${comment.content_id}" news-id="${comment.news_id}">
                <textarea class="reply_input"></textarea>
                <input type="button" value="回复" class="reply_btn right_float">
                <input type="reset" name="" value="取消" class="reply_cancel right_float">
              </form>

          </li>`;
                    message.showSuccess('评论成功!');
                    setTimeout(() => {
                        $(".comment-list").prepend(html_comment);
                    }, 800);

                    // 清空
                    $newsComment.val('');

                } else if (res.errno === '4101') {
                    // 用户未登录
                    message.showError(res.errmsg);
                    setTimeout(() => {
                        window.location.href = '/login/'
                    }, 800)

                } else {
                    message.showError(res.errmsg);
                }
            })

            .fail(() => {
                message.showError('服务器超时,请重试!');
            })

    })


});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值