(二十)首页内容详细【详细页】+ 评论 + 回复

1. 首页点瀑布流中的一个 进入详细页
  在详细页的
onLoad里向后端发送请求

2. 后端api

一 详细页面的展示 

1. onLoad

// 在首页页面的代码
<navigator url="/pages/newsDetail/newsDetail?newsId={{item.id}}"><image class="img" src="{{item.cover}}" mode="widthFix"></image></navigator>
// 详细页
Pages({
  /**
   * 页面的初始数据
   */
  data: {
    news:{}
  },

  /**
   * 生命周期函数--监听页面加载
   * 
   * 获取页面详细内容
   */
  onLoad: function (options) {
    var newsId = options.newsId
    console.log(newsId)
    wx.request({
    //自己写的url url: api.Newsdetail
+ newsId + '/', method: 'GET', dataType: 'json', responseType: 'text', success: (res)=> { if (res.statusCode===200){ this.setData({ news:res.data }) } }, }) }, })

2. 后端API

url(r'^newsdetail/(?P<pk>d+)/$', news.NewsViewDetail.as_view()),

view.py

class NewsModelViewDetail(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()
    topic = serializers.SerializerMethodField()
  
  # 整合我们要的字段的格式 create_date
= serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S') images = serializers.SerializerMethodField() viewer = serializers.SerializerMethodField() comment = serializers.SerializerMethodField() class Meta: model = models.News exclude = ['cover', ] def get_user(self, obj): return model_to_dict(obj.user, fields=['id', 'nickname', 'avatar']) def get_topic(self, obj): if not obj.topic: return return model_to_dict(obj.topic, fields=['id', 'title']) def get_images(self, obj): detail_queryset = models.NewsDetail.objects.filter(news=obj) # return [{'id':item.id,'path':item.cos_path} for item in detail_queryset] # return [item.cos_path for item in detail_queryset] return [model_to_dict(item, fields=['id', 'cos_path']) for item in detail_queryset] def get_viewer(self, obj): ''''要统计浏览人的总个数 你可以自己一个一个加到数据库,也可以像下面没有注释的方法''' # 根据动态的对象 obj(news) # viewer_query = models.ViewerRecord.objects.filter(news=obj).order_by("-id")[0:10] # return [model_to_dict(item.user, fields=['nickname', 'avatar']) for item in viewer_query] queryset = models.ViewerRecord.objects.filter(news=obj) viewer_list = queryset.order_by('-id')[0:10] context = { 'count': queryset.count(), 'result': [model_to_dict(item.user, fields=['nickname', 'avatar']) for item in viewer_list] } return context def get_comment(self, obj): ''' 获取评论的第一条评论,在获取每个评论的第一条的二级评论【也就是展示一条第一级评论在展示一条对一级评论的评论】 :param obj: :return: ''' # 1.首先获取所有的一级评论 下面可以用model_to_dict 也可以直接取 value first_queryset = models.CommentRecord.objects.filter(news=obj, depth=1).order_by('-id').values( 'id', 'content', 'depth', 'user__nickname', 'user__avatar', 'create_date' ) # 2. 获取二级评论的所有评论 ''' second_queryset = models.CommentRecord.objects.filter(news=obj, depth=2).order_by('-id').values( 'id', 'content', 'user__nickname', 'user__avatar', 'create_date' ) # 2. 获取一级评论下的 二级评论 # first_id_list = [item['id'] for item in first_queryset] # second_queryset = models.CommentRecord.objects.filter(news=obj, depth=2, reply_id__in=first_id_list).order_by('-id').values('id','content','user__nickname','user__avatar','create_date') ''' # 2. 获取一级评论下的 二级评论(每个二级评论只取最新的一条) # 获取一级评论的 id first_id_list = [item['id'] for item in first_queryset] from django.db.models import Max # 到 values处 就是先取出一级评论下的所有二级评论,在取一个最大的 就是分组取最大 result = models.CommentRecord.objects.filter(news=obj, depth=2, reply_id__in=first_id_list).values( 'reply_id').annotate(max_id=Max('id')) second_id_list = [item['max_id'] for item in result] # 获取二级评论 second_queryset = models.CommentRecord.objects.filter(id__in=second_id_list).values( 'id', 'content', 'depth', 'user__nickname', 'user__avatar', 'create_date', 'reply_id', 'reply__user__nickname' ) '''这个时候我们把第一级评论和第二级评论合并 请看脚本 处理评论结构化 1. 字典的有序 2. 看下面代码 ''' import collections first_dict = collections.OrderedDict() first_dict = {} for item in first_queryset: item['create_date'] = item['create_date'].strftime('%Y-%m-%d %H:%M:%S') first_dict[item['id']] = item for node in second_queryset: node['create_date'] = node['create_date'].strftime('%Y-%m-%d %H:%M:%S') first_dict[node['reply_id']]['child'] = [node, ] return first_dict.values() # ① RetrieveAPIView 会自动查出数据库的内容 然后在走我们自定义的序列化 class NewsViewDetail(RetrieveAPIView): queryset = models.News.objects serializer_class = NewsModelViewDetail

然后拿到数据之后我们在前端页面展示就可以啦  这里我不在赘述,后面还会有

二 评论

上面是每一条一级评论展示一条二级评论 但是我们要看到很多的评论的话 我们就写一个下面的代码

#                                传过去一级评论的id           一级评论的索引 目的是要替换索引位置的值
<text class="click_more" bindtap="click_more_content" data-root="{{item.id}}" data-parent_id='{{parent_id}}'>点击查看更多</text>
# 获取二级评论的所有值
url(r'^comment/$', comment.CommentView.as_view()),
######################################################################
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import ListAPIView
from rest_framework import serializers
from django.forms import model_to_dict
from app01 import models


'''
# 最开始用的这种方式 但是出来的字段和最开始不一样,所以用下面的方式
class CommenModelSerializert(serializers.ModelSerializer):
create_date = serializers.DateTimeField('%Y-%m-%d')
user = serializers.SerializerMethodField()
reply_user = serializers.SerializerMethodField()

class Meta:
model = models.CommentRecord
fields = '__all__'

def get_user(self, obj):
return model_to_dict(obj.user, fields=['id', 'nickname', 'avatar'])

def get_reply_user(self, obj):
return model_to_dict(obj.reply.user, fields=['id', 'nickname'])

'''
class CommenModelSerializert(serializers.ModelSerializer):
create_date = serializers.DateTimeField('%Y-%m-%d')
user__nickname = serializers.CharField(source='user.nickname')
user__avatar = serializers.CharField(source='user.avatar')
reply_id = serializers.CharField(source='reply.id')
reply__user__nickname = serializers.CharField(source='reply.user.nickname')

class Meta:
model = models.CommentRecord
# fields = '__all__'
exclude = ['news', 'user', 'reply', 'root']

# 第一步 先看这
class CommentView(APIView):
def get(self, request, *args, **kwargs):
root_id = request.query_params.get('root_id')
comment_query = models.CommentRecord.objects.filter(root_id=root_id).order_by('id')

ser = CommenModelSerializert(instance=comment_query, many=True)

return Response(ser.data)
 
  /**
   * 点击获取更多二级评论
   */
  click_more_content:function(res){
    var root_id = res.currentTarget.dataset.root;
    var parent_id = res.currentTarget.dataset.parent_id;

    wx.request({
      url: api.CommentAPI,
      data: {
      root_id:root_id
      },
      method: 'GET',
      dataType: 'json',
      responseType: 'text',
      success: (res) => {
        this.setData({
      // 找到所在的位置,然后替换 [
'news.comment['+ parent_id +'].child']:res.data }) }, }) },

三 回复

 前端

 <view class="comment">
    <view>全部评论 - {{news.comment.length}} </view>
    <view class="tree">
      <view class="item" wx:for="{{news.comment}}" wx:key="id" wx:for-index="parent_id">
        <image class="big-avatar" src="{{item.user__avatar}}"></image>
        <view class="body">
          <view class="user">

            <view class="name">
              <text>{{item.user__nickname}}</text>
              <text>{{item.create_date}}</text>
            </view>

            <view class="func">
              <text 
              data-news="{{news.id}}" 
              data-reply="{{item.id}}" 
              data-depth="{{item.depth + 1}}" 
              data-nickname="{{item.user__nickname}}"
              data-root="{{item.id}}" 
              bindtap="onClickShowCommentModal">回复</text>

              <text wx:if="{{item.favor}}" class="red">赞</text>

              <text wx:else>赞</text>
            </view>
          </view>
          <!-- 一级评论内容 -->
          <view class="content">
            ## {{item.content}}
          </view>
          <!-- 二级评论内容 -->
          <view class="reply" wx:if="{{item.child}}">
            <view class="row" wx:for="{{item.child}}" wx:key="id" wx:for-item="row">
              <view class="reply-menu">
                <view class="reply-user">
                  <image class="small-avatar" src="{{row.user__avatar}}"></image>
                  <view class="reply-name">
                    <text>#{{row.user__nickname}}</text>
                    <text>{{row.create_date}}</text>
                  </view>
                </view>
                <view class="reply-func">
                // 文章的id 回复的id 深度 +1 头像 根id
                  <text 
                  data-news="{{news.id}}" 
                  data-reply="{{row.id}}" 
                  data-depth="{{row.depth + 1}}" 
                  data-nickname="{{row.nickname}}" 
                  data-root="{{item.id}}"
                  bindtap="onClickShowCommentModal">回复</text>

                  <text wx:if="{{row.favor}}" class="red">赞</text>
                  <text wx:else>赞</text>
                </view>
              </view>
              <view class="reply-content">{{row.content}}</view>

              <text class="click_more" 
              bindtap="click_more_content" 
              data-root="{{item.id}}" 
              data-parent_id='{{parent_id}}'>点击查看更多</text>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
</view>

<!-- 回复 -->
<view class="buttom-view">
  <view class="comment-area" wx:if="{{isShowCommentModal}}">
    <view class="top">
      <image class="big-avatar" src="{{news.user.avatar}}"></image>
      <text>评论</text>
      <!-- 回复一级评论显示一级评论的名字 点击回复按钮显示 -->
      <view class="reply" wx:if="{{reply.reply}}">回复 {{reply.nickname}}
        <icon type="clear" size="15" bindtap="onClickClearReply"></icon>
      </view>
    </view>
    <textarea fixed="true" placeholder="评论内容..." bindinput="inputComment"></textarea>
    <view class="btn">
      <view class="publish" bindtap="onClickPostComment">发布</view>
    </view>

     <!-- 关闭说点什么 -->
    <view class="hide">
      <icon type="cancel" size="30" bindtap="onClickCancelCommentModal"></icon>
    </view>
  </view>
  <!-- 说点什么 -->
  <view class="text-input" wx:else>
    <image class="big-avatar" src="/static/girl.jpg"></image>
    <input placeholder="说点什么..." bindtap="onClickShowCommentModal" data-news="{{news.id}}" data-depth="{{1}}"></input>
  </view>
  <!-- 最开头的view -->
</view>

                    
前端页面
// pages/newsDetail/newsDetail.js
var api = require('../../config/api.js')
Page({

  /**
   * 页面的初始数据
   */
  data: {
    news:{},
    isShowCommentModal:false,
    reply:{}
  },

  /**
   * 生命周期函数--监听页面加载
   * 
   * 获取页面详细内容
   */
  getNewsDetail(newsid){
    wx.request({
      url: api.Newsdetail + newsid + '/',
      method: 'GET',
      dataType: 'json',
      responseType: 'text',
      success: (res) => {
        console.log('第28行', res.data)
        if (res.statusCode === 200) {
          this.setData({
            news: res.data
          })
        }
      },
    })
  },
  onLoad: function (options) {
    var newsid = options.newsId;
    this.getNewsDetail(newsid);
  },
  /**
   * 点击获取更多二级评论
   */
  click_more_content:function(res){
    var root_id = res.currentTarget.dataset.root;
    var parent_id = res.currentTarget.dataset.parent_id;
    wx.request({
      url: api.CommentAPI,
      data: {
      root_id:root_id
      },
      method: 'GET',
      dataType: 'json',
      responseType: 'text',
      success: (res) => {
        console.log('52',res.data)
        this.setData({
          ['news.comment['+ parent_id +'].child']:res.data
        })
      },
    })
  },
  /**
   * 点击获取输入框 
   */
  onClickShowCommentModal:function(e){
    // {depth: 1, news: 40}
    // {depth: 2, news: 40, nickname: "晓强2", reply: 21, root: 21}
    console.log(e)
    console.log('65',e.currentTarget.dataset)
    this.setData({
      isShowCommentModal:true,
      // 回复一级评论显示一级评论的名字
      reply: e.currentTarget.dataset
    })
    // 自己写的时候有bug 重新获取一下
    var news_id = e.currentTarget.dataset.news
    this.getNewsDetail(news_id)
  },
  /**
  * 点击关闭输入框 
  */
  onClickCancelCommentModal: function () {
    this.setData({
      isShowCommentModal: false
    })
  },
  /**
   * 填写评论内容
   */
  inputComment:function(e){
    // console.log(e.detail.value)
    this.setData({
      ["reply.content"]:e.detail.value
    })
  },
  /**
   * 发布
   */
  onClickPostComment:function(){
    if (!this.data.reply.content){
      wx.showToast({
        title: '请填写评论',
        icon: 'none',
      })
      return
    }
    wx.request({
      url: api.CommentAPI,
      data: {
        depth:this.data.reply.depth,
        news: this.data.reply.news,       // 回复动态的id
        // nenicknamews: this.data.reply.nickname,
        content: this.data.reply.content,
        reply: this.data.reply.reply,
        root: this.data.reply.root,
      },
      method: 'POST',
      dataType: 'json',
      responseType: 'text',
      success: (res)=> {
        console.log('113',res.data)
        if(res.statusCode==201){
          if (this.data.reply.parent_id == undefined){
            // 如果是根评论
            var dataList = this.data.news.comment
            dataList.unshift(res.data)
            // this.data.news.comment.push()       push     加到最下面
            // this.data.news.comment.unshift()    unshift  加到最上面
            this.setData({
              ['news.comment']: dataList,
              ['news.comment_count']: this.data.news.comment_count + 1
            })
            this.onClickCancelCommentModal()
          }else{
            // 如果是子评论
            // 1. 根据索引获取二级评论        注意 后台数据要有一级评论要有child字段
            var childcomment = this.data.news.comment[this.data.reply.parent_id].child;
            console.log(123)
            console.log(childcomment)
            console.log(res.data)
            childcomment.unshift(res.data)
            console.log('ssssss',this.data.news)
            this.setData({
              ['news.comment[' + this.data.reply.parent_id + '].child']: childcomment,
              ['news.comment_count']: this.data.news.comment_count + 1
            })
            this.onClickCancelCommentModal()
          }
        }
      },
    })
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})
前端js
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import ListAPIView
from rest_framework import serializers
from django.forms import model_to_dict
from app01 import models
from rest_framework import status
from django.db.models import F


'''
class CommenModelSerializert(serializers.ModelSerializer):
    create_date = serializers.DateTimeField('%Y-%m-%d')
    user = serializers.SerializerMethodField()
    reply_user = serializers.SerializerMethodField()

    class Meta:
        model = models.CommentRecord
        fields = '__all__'

    def get_user(self, obj):
        return model_to_dict(obj.user, fields=['id', 'nickname', 'avatar'])

    def get_reply_user(self, obj):
        return model_to_dict(obj.reply.user, fields=['id', 'nickname'])

'''
class CommenModelSerializert(serializers.ModelSerializer):
    create_date = serializers.DateTimeField('%Y-%m-%d')
    user__nickname = serializers.CharField(source='user.nickname')
    user__avatar = serializers.CharField(source='user.avatar')
    reply_id = serializers.CharField(source='reply.id')
    reply__user__nickname = serializers.CharField(source='reply.user.nickname')

    class Meta:
        model = models.CommentRecord
        # fields = '__all__'
        exclude = ['news', 'user', 'reply', 'root']



class CreateCommenModelSerializert(serializers.ModelSerializer):
    create_date = serializers.DateTimeField('%Y-%m-%d', read_only=True)
    user__nickname = serializers.CharField(source='user.nickname', read_only=True)
    user__avatar = serializers.CharField(source='user.avatar', read_only=True)
    reply_id = serializers.CharField(source='reply.id', read_only=True)
    reply__user__nickname = serializers.CharField(source='reply.user.nickname', read_only=True)

    class Meta:
        model = models.CommentRecord
        # fields = '__all__'
        exclude = ['user', 'favor_count']



class CommentView(APIView):
    def get(self, request, *args, **kwargs):
        root_id = request.query_params.get('root_id')

        comment_query = models.CommentRecord.objects.filter(root_id=root_id).order_by('id')

        ser = CommenModelSerializert(instance=comment_query, many=True)

        return Response(ser.data)

    def post(self, request, *args, **kwargs):

        # 1.进行数据校验    序列化 news/ depth/reply/content/root
        ser = CreateCommenModelSerializert(data=request.data)
        # 2.校验通过 保存数据库
        if ser.is_valid():
            # 保存到数据库
            ser.save(user_id=1)
            # 对新增的数据值进行序列化【数据格式要调整 参考上面的read_only创建的时候校验,传过去不校验】
            print(ser.data)
            new_id= ser.data.get('news')
            models.News.objects.filter(id=new_id).update(comment_count=F('comment_count')+1)
            return Response(ser.data,status=status.HTTP_201_CREATED)
        return Response(ser.errors)
        # 3.将保存到数据库的数据再返回小程序页面(小程序页面要展示)
后端逻辑

后续会添加  点赞文章 点赞评论 关注等

原文地址:https://www.cnblogs.com/a438842265/p/12471259.html