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 () { } })
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.将保存到数据库的数据再返回小程序页面(小程序页面要展示)
后续会添加 点赞文章 点赞评论 关注等