06 新闻详情页
一、功能需求分析
1、功能
- 新闻详情
- 评论加载
- 评论添加
二、新闻详情页
1、接口设计
-
接口说明
项目项目 说明说明 请求方法 GET
url
/news/<int:news_id>/
参数类型 查询参数 -
返回结果
新闻详情html
2、后端代码
def news_detial(request, news_id):
if News.objects.filter(id=news_id, is_delete=False).exists():
return render(request, 'news/news_detail.html',context={'news_id': news_id})
else:
return HttpResponseNotFound('<h1>Page not found</h1>')
3、前端html
{% extends 'base/base.html' %}
{% load static %}
{% block title %}新闻详情{% endblock %}
{% block link %}
<link rel="stylesheet" href="{% static 'css/news/news-detail.css' %}">
{% endblock %}
{% block main_contain %}
<div class="news-contain" news_id="{{ news_id }}">
<h1 class="news-title"></h1>
<div class="news-info">
<div class="news-info-left">
<span class="news-author">作者</span>
<span class="news-pub-time">1小时前</span>
<span class="news-type">类型</span>
</div>
</div>
<article class="news-content">
文章内容
</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="请登录后参加评论" readonly>
</div>
{% endif %}
<button class="comment-btn">发表评论</button>
{% csrf_token %}
</div>
<ul class="comment-list">
</ul>
</div>
</div>
{% endblock %}
{% block otherjs %}
<script src="{% static 'js/base/message.js' %}"></script>
<script src="{% static 'js/news/detial.js' %}"></script>
{% endblock otherjs %}
三、新闻详情
1、接口设计
-
接口说明
项目项目 说明说明 请求方法 GET
url
/news/detiallist/
参数类型 查询参数 -
参数说明
参数名 类型 是否必须 描述 news_id 整型 是 新闻id -
返回结果
{ "error": "0", "errmsg": "", "data": { 'news_id': news_id, 'news':{ 'title': title, 'content': content, 'update_time': update_time, 'tag_name': tag_name, 'author_username': author_username } } }
2、后端代码
def news_detial_list(request):
try:
news_id = int(request.GET.get('news_id', 1))
except Exception as e:
logger.error('新闻id错误:\n{}'.format(e))
news_id = 1
news = News.objects.values('title', 'content', 'update_time').annotate(
tag_name=F('tag__name'),
author_username=F('author__username')).filter(id=news_id, is_delete=False)
news_list = list(news)
if news_list:
data = {
'news_id': news_id,
'news': news_list
}
return json_response(data=data)
else:
return json_response(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
路由
path('news/detiallist/', views.news_detial_list, name='detial_list'),
四、评论
1、模型设计
class Comments(BaseModel):
"""
评论模型
"""
content = models.TextField('内容', help_text='内容')
author = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)
news = models.ForeignKey('News', on_delete=models.CASCADE)
# 本项目设计二级评论,修改Comments模型,添加一个parent字段
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
class Meta:
ordering = ['-update_time', '-id'] # 排序
db_table = "tb_comments" # 指明数据库表名
verbose_name = "评论" # 在admin站点中显示的名称
verbose_name_plural = verbose_name # 显示的复数名称
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
2、接口设计
-
接口说明
项目项目 说明说明 请求方法 GET
url
/news/comments/
参数类型 查询参数 -
参数说明
参数名 类型 是否必须 描述 news_id 整型 是 新闻id -
返回结果
{ "error": "0", "errmsg": "", "data": { 'news_id': news_id, 'comments':{ 'id': id, 'content': content, 'update_time': update_time, 'author_username': author_username, 'parent_author_username': parent_author_username, 'parent_content': parent_content, 'parent_update_time': parent_update_time, } } }
3、后端代码
def comment_list(request):
try:
news_id = int(request.GET.get('news_id', 1))
except Exception as e:
logger.error('新闻id错误:\n{}'.format(e))
news_id = 1
comments = Comments.objects.values('id',
'content', 'update_time',).annotate(
author_username=F('author__username'),
parent_author_username=F('parent__author__username'),
parent_content=F('parent__content'),
parent_update_time=F('parent__update_time'),
).filter(news_id=news_id, is_delete=False)
# print(list(comments))
data = {
'news_id': news_id,
'comments': list(comments)
}
return json_response(data=data)
路由
path('news/comments/', views.comment_list, name='comments_list'),
导入数据
五、添加评论
1、接口设计
1、接口说明
项目项目 | 说明说明 |
---|---|
请求方法 | POST |
url |
/news/<int:news_id>/addcomments/ |
参数类型 | 查询参数 |
2、参数说明
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
news_id | 整数 | 是 | 新闻id |
content | 字符串 | 是 | 新闻评论内容 |
parent_id | 整数 | 否 | 父评论id |
注意:post请求需要携带csrftoken
-
返回结果
{ "error": "0", "errmsg": "", "data": { 'news_id': news_id, 'content_id': id, 'content': content, 'author': author, 'update_time': update_time 'parent': { 'news_id': news_id, 'content_id': id, 'content': content, 'author': author, 'update_time': update_time 'parent': null } } }
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
news_id | 整数 | 是 | 新闻id |
content | 字符串 | 是 | 新闻评论内容 |
parent_id | 整数 | 否 | 父评论id |
注意:post请求需要携带csrftoken
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
news_id | 整数 | 是 | 新闻id |
content | 字符串 | 是 | 新闻评论内容 |
parent_id | 整数 | 否 | 父评论id |
注意:post请求需要携带csrftoken
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
news_id | 整数 | 是 | 新闻id |
content | 字符串 | 是 | 新闻评论内容 |
parent_id | 整数 | 否 | 父评论id |
注意:post请求需要携带csrftoken
2、后端代码
def add_comment(request,news_id):
if request.method == 'POST':
# 是否登录
if not request.user.is_authenticated:
return json_response(errno=Code.SESSIONERR, errmsg=error_map[Code.SESSIONERR])
form = Checkaddcomments(request.POST)
if form.is_valid():
# 校验成功
# 将评论写入数据库
# 保存到数据库
news_id = form.cleaned_data.get('news_id')
content = form.cleaned_data.get('content')
parent_id = form.cleaned_data.get('parent_id')
new_comment = Comments()
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
new_comment.save()
return json_response(data=new_comment.to_dict_data())
else:
err_msg_str = form.get_errors()
return json_response(errno=Code.PARAMERR, errmsg=err_msg_str)
路由
path('news/<int:news_id>/addcomments/', views.add_comment, name='addcomments'),
六、前端设计
1、前端js
/*=== newsdetialStart ===*/
$(function () {
fn_load_newsdetial();
console.log($('.news-contain').attr('news_id'));
function fn_load_newsdetial() {
let $news_id = $('.news-contain').attr('news_id');
$
.ajax({
url:'/news/detiallist/?news_id=' + $news_id,
type:'GET',
dataType:'json'
})
.done((resp)=>{
if(resp.errno === '0')
{
let $news = resp.data.news[0];
$('.news-title').html($news.title);
$('.news-author').html($news.author_username);
$('.news-pub-time').html($news.update_time);
$('.news-type').html($news.tag_name);
$('.news-content').html($news.content);
}
else
{
message.showError(resp.errmsg)
}
})
.fail(()=>{
message.showError('服务器超时,请重试!')
})
}
});
/*=== newsdetialEnd ===*/
/*=== commentsStart ===*/
$(function () {
fn_load_comments();
function fn_load_comments() {
let $news_id = $('.news-contain').attr('news_id');
$
.ajax({
url:'/news/comments/?news_id=' + $news_id,
type:'GET',
dataType:'json'
})
.done((resp)=>{
if(resp.errno === '0')
{
// message.showSuccess('评论获取成功');
resp.data.comments.forEach(function (comment) {
let $parent=``;
if(comment.parent_author_username)
{
$parent = `<div class="parent_comment_text">
<div class="parent_username">${comment.parent_author_username}</div>
<div class="comment_time">${comment.parent_update_time}</div>
<div class="parent_content_text">
${comment.parent_content}
</div>
</div>`;
}
let content = `<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.content}</div>`
+ $parent +
`<a href="javascript:void(0);" class="reply_a_tag right_float">回复</a>
<form class="reply_form left_float" comment-id="${comment.id}"
news-id="${resp.data.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>`;
$('.comment-list').append(content);
})
}
else
{
message.showError(resp.errmsg)
}
})
.fail(()=>{
message.showError('服务器超时,请重试!')
})
}
});
// 修改static/js/news/news_detail.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) {
// 评论
let $this = $(this);
let parent_id = $this.parent().attr('comment-id');
let content = $this.prev().val();
let news_id = $('.news-contain').attr('news_id');
if (!content) {
message.showError('请输入评论内容!');
return
}
$
.ajax({
url: '/news/' + news_id + '/addcomments/',
type: 'POST',
data: {
news_id:news_id,
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 = $('.news-contain').attr('news_id');
let content = $newsComment.val();
if (!content) {
message.showError('请输入评论内容!');
return
}
$
.ajax({
url: '/news/' + news_id + '/addcomments/',
type: 'POST',
data: {
news_id:news_id,
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 = '/user/login/'
}, 800)
} else {
message.showError(res.errmsg);
}
})
.fail(() => {
message.showError('服务器超时,请重试!');
})
})
});
网友评论