美文网首页微信小程序
1.6 收藏、评论、点赞、计数功能准备工作

1.6 收藏、评论、点赞、计数功能准备工作

作者: 追梦小乐 | 来源:发表于2018-12-11 09:23 被阅读44次

    1、收藏、评论、点赞、计数功能准备工作

    编写收藏、评论、点赞的功能按钮。计数功能是一个被动功能,无须用户有意识的主动触发。

    1.1 编写3个功能的功能按钮

    post-detail.wxml
    <view class='tool'>
      <view class='tool-item' catchtap='onUpTap' data-post-id="{{post.postId}}">
        <image src='/images/icon/wx_app_like.png'></image>
        <text>{{post.upNum}}</text>
      </view>
    
      <view class='tool-item comment' catchtap='onCommentTap' data-post-id="{{post.postId}}">
        <image src='/images/icon/wx_app_message.png'></image>
        <text>{{post.commentNum}}</text>
      </view>
    
      <view class='tool-item' catchtap='onCollectionTap' data-post-id="{{post.postId}}">
        <image src='/images/icon/wx_app_collect.png'></image>
        <text>{{post.collectionNum}}</text>
      </view>
    </view>
    

    1.2 编写3个功能的功能样式

    post-detail.wxss
    .tool{
    height: 64rpx;
    text-align: center;
    line-height: 64rpx;
    margin: 20rpx 28rpx 20rpx 0;
    }
    
    .tool-item{
     display: inline-block; 
     vertical-align: top; 
    margin-right: 30rpx;
    }
    
    .tool-item image{
      height: 30rpx;
      width: 30rpx;
      vertical-align: -3px;
      margin-right: 10rpx;
    }
    
    .comment image{
      transform: scale(0.85);
    }
    
    image.png
    • 属性注意点

    1)transform: scale()属性


    image.png image.png

    2)display: inline-block;属性


    image.png image.png

    2、文章收藏功能

    文章收藏功能需要记录两个变量值:
    1)根据是否已经收藏更换照片、
    2)收藏数量的变化

    2.1 条件渲染:wx:if 与 wx:else

    小程序提供了wx:if 与 wx:else来实现条件渲染。当变量为true时,执行wx:if,否则将执行wx:else

    2.1.1 条件渲染:wx:if 与 wx:else
    post-detail.wxml
    
      <view class='tool-item' catchtap='onCollectionTap' data-post-id="{{post.postId}}">
        <image wx:if="{{post.collectionStatus}}" src='/images/icon/wx_app_collected.png'></image>
        <image wx:else src='/images/icon/wx_app_collected.png'></image>
        <text>{{post.collectionNum}}</text>
      </view>
    
    image.png
    2.1.2 多级别 if else
    <view wx:if="{{length > 5}}">1</view>
    <view wx:elif="{{length > 2}}">2</view>
    <view wx:else>3</view>
    

    2.2 实现收藏点击功能

    2.2.1 添加处理文章收藏的方法

    首先完善数据库工具类的操作

    DBPost.js
    
      //收藏文章
      collect() {
        return this.updatePostData("collect");
      }
    
      //更新本地的点赞、评论信息、收藏、阅读量
      updatePostData(category) {
        var itemData = this.getPostItemById(),
          postData = itemData.data,
          allPostData = this.getAllPostData();
    
        switch (category) {
          case 'collect':
            //处理收藏
            if (!postData.collectionStatus) {
              //如果当前状态是未收藏
              postData.collectionNum++;
              postData.collectionStatus = true;
            } else {
              //如果当前状态是收藏
              postData.collectionNum--;
              postData.collectionStatus = false;
            }
            break;
          default:
            break;
        }
    
    
    2.2.2 编写onCollectionTap方法

    首先完善数据库工具类的操作

    post-detail.js
      onCollectionTap:function(event){
        var newData = this.dbPost.collect();
    
        //重新绑定数据
        //应该选择更新部分数据
        this.setData({
          'post.collectionStatus':newData.collectionStatus,
          'post.collectionNum': newData.collectionNum
        });
      }
    

    2.3 交互反馈wx:showToast

    小程序提供了一些交互反馈API来帮助开发者处理交互相关的问题,目前主要有如下几个:


    image.png
    2.3.1 文章收藏功能的交互反馈
    post-detail.js
        wx.showToast({
          title: newData.collectionStatus? '收藏成功':'取消成功',
          duration:1000,
          icon:'success',
          mask:true
        })
    

    -参数说明


    image.png image.png

    2.3 文章点赞功能

    这个功能跟之前文章收藏功能基本写法是一样的

    2.3.1 添加处理点赞操作的方法
    DBPost.js
    
      //点赞或者取消点赞
      up() {
        var data = this.updatePostData('up');
        return data;
      }
    
      //更新本地的点赞、评论信息、收藏、阅读量
      updatePostData(category) {
        var itemData = this.getPostItemById(),
          postData = itemData.data,
          allPostData = this.getAllPostData();
    
        switch (category) {
          case 'collect':
            //处理收藏
            if (!postData.collectionStatus) {
              //如果当前状态是未收藏
              postData.collectionNum++;
              postData.collectionStatus = true;
            } else {
              //如果当前状态是收藏
              postData.collectionNum--;
              postData.collectionStatus = false;
            }
            break;
          case 'up':
            if (!postData.upStatus) {
              postData.upNum++;
              postData.upStatus = true;
            } else {
              postData.upNum--;
              postData.upStatus = false;
            }
            break;
          default:
            break;
        }
    
        //更新缓存数据库
        allPostData[itemData.index] = postData;
        this.execSetStorageSync(allPostData);
        return postData;
      }
    
    
    2.3.2 编写onUpTap方法
    post-detail.js
      onUpTap: function (event){
        var newData = this.dbPost.up();
    
        this.setData({
          'post.upStatus': newData.upStatus,
          'post.upNum': newData.upNum
        });
        wx.showToast({
          title: newData.collectionStatus ? '点赞成功' : '取消成功',
          duration: 1000,
          icon: 'success',
          mask: false
        })
      },
    
    
    2.3.3 点赞功能的条件渲染
    post-detail.wxml
    <view class='tool'>
      <view class='tool-item' catchtap='onUpTap' data-post-id="{{post.postId}}">
        <image wx:if="{{post.upStatus}}" src='/images/icon/wx_app_liked.png'></image>
         <image wx:else src='/images/icon/wx_app_like.png'></image>
        <text>{{post.upNum}}</text>
      </view>
    
    image.png

    2.4 本地缓存的重要性及应用举例

    提供本地的key&value缓存机制是小程序的一大特点,善用本地缓存可以极大地改善客户端的体验与服务器的性能。

    在一个高性能的产品中,缓存的重要性是不言而喻的,建议开发者将本地缓存视作为一个本地的key&value数据库,并封装一些类和公用方法,提供给项目中的各个调用方,最好不要让getStorage 、setStorage等方法充斥在项目的每一个角落

    2.5 支持文字、图片、拍照、语音上传的文章评论

    2.5.1 注册post-comment页面
    app.json
    image.png
    2.5.2 onCommentTap方法
    post-detail.js
      onCommentTap: function (event){
        var id = event.currentTarget.dataset.postId;
        wx.navigateTo({
          url: '../post-comment/post-comment?id='+id
        })
      },
    

    2.6 文章评论页面的实现步骤与思路

    2.6.1 思路

    1)加载并显示当前文章已存在的评论
    2)添加新评论的功能

    2.6.2 步骤

    1)在post-comment.js中获取并绑定文章评论数据
    2)在post-comment 页面的wxml和wcss显示文章评论数据
    3)编写添加新评论的功能

    2.7 获取并绑定文章评论数据

    2.7.1 获取评论数据
    post-comment.js
    import{DBPost} from '../../../db/DBPost.js'
    
    Page({
    
      /**
       * 页面的初始数据
       */
      data: {
      
      },
    
      /**
       * 生命周期函数--监听页面加载
       */
      onLoad: function (options) {
        var postId = options.id;
        this.dbPost = new DBPost(postId);
        var comments = this.dbPost.getCommentData();
    
        console.log('comments==' + comments)
    
        //绑定评论数据
        this.setData({
          comments:comments
        })
      },
    
     
    })
    
    2.7.2 编写获取文章评论的方法
    DBPost.js
      //编写获取文章评论的方法
      getCommentData() {
        var itemData = this.getPostItemById().data;
        //按时间减序排列评论
        itemData.comments.sort(this.compareWithTime);
        var len = itemData.comments.length,
          comment;
    
        for (var i = 0; i < len; i++) {
          comment = itemData.comments[i];
          comment.create_time = util.getDiffTime(comment.create_time, true);
        }
        return itemData.comments;
      }
    
    2.7.3 compareWithTime方法
    DBPost.js
     compareWithTime(value1, value2) {
        var flag = parseFloat(value1.create_time) - parseFloat(value2.create_time);
    
        if (flag < 0) {
          return 1;
        } else if (flag > 0) {
          return -1;
        } else {
          return 0;
        }
      }
    
    2.7.4 getDiffTime方法
    util.js
    /*
     *根据客户端的时间信息得到发表评论的时间格式
     *多少分钟前,多少小时前,然后是昨天,然后再是月日
     * Para :
     * recordTime - {float} 时间戳
     * yearsFlag -{bool} 是否要年份
     */
    function getDiffTime(recordTime, yearsFlag) {
      if (recordTime) {
        recordTime = new Date(parseFloat(recordTime) * 1000);
        var minute = 1000 * 60,
          hour = minute * 60,
          day = hour * 24,
          now = new Date(),
          diff = now - recordTime;
        var result = '';
        if (diff < 0) {
          return result;
        }
        var weekR = diff / (7 * day);
        var dayC = diff / day;
        var hourC = diff / hour;
        var minC = diff / minute;
        if (weekR >= 1) {
          var formate = 'MM-dd hh:mm';
          if (yearsFlag) {
            formate = 'yyyy-MM-dd hh:mm';
          }
          return recordTime.format(formate);
        }
        else if (dayC == 1 || (hourC < 24 && recordTime.getDate() != now.getDate())) {
          result = '昨天' + recordTime.format('hh:mm');
          return result;
        }
        else if (dayC > 1) {
          var formate = 'MM-dd hh:mm';
          if (yearsFlag) {
            formate = 'yyyy-MM-dd hh:mm';
          }
          return recordTime.format(formate);
        }
        else if (hourC >= 1) {
          result = parseInt(hourC) + '小时前';
          return result;
        }
        else if (minC >= 1) {
          result = parseInt(minC) + '分钟前';
          return result;
        } else {
          result = '刚刚';
          return result;
        }
      }
      return '';
    }
    
    
    2.7.5 在Date原型链上新增format方法
    util.js
    /*
     *拓展Date方法。得到格式化的日期形式
     *date.format('yyyy-MM-dd'),date.format('yyyy/MM/dd'),date.format('yyyy.MM.dd')
     *date.format('dd.MM.yy'), date.format('yyyy.dd.MM'), date.format('yyyy-MM-dd HH:mm')
     *使用方法 如下:
     *                       var date = new Date();
     *                       var todayFormat = date.format('yyyy-MM-dd'); //结果为2015-2-3
     *Parameters:
     *format - {string} 目标格式 类似('yyyy-MM-dd')
     *Returns - {string} 格式化后的日期 2015-2-3
     *
     */
    (function initTimeFormat() {
      Date.prototype.format = function (format) {
        var o = {
          "M+": this.getMonth() + 1, //month
          "d+": this.getDate(), //day
          "h+": this.getHours(), //hour
          "m+": this.getMinutes(), //minute
          "s+": this.getSeconds(), //second
          "q+": Math.floor((this.getMonth() + 3) / 3), //quarter
          "S": this.getMilliseconds() //millisecond
        }
        if (/(y+)/.test(format)) format = format.replace(RegExp.$1,
          (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        for (var k in o) if (new RegExp("(" + k + ")").test(format))
          format = format.replace(RegExp.$1,
            RegExp.$1.length == 1 ? o[k] :
              ("00" + o[k]).substr(("" + o[k]).length));
        return format;
      };
    })()
    
    
    2.7.6 添加module.exports
    util.js
    module.exports = {
      getDiffTime: getDiffTime
    }
    
    2.7.7 引用util模块
    DBPost.js
    var util = require('../util/util.js')
    
    image.png

    2.8 显示文章评论数据

    2.8.1 获取评论数据wxml代码
    post-comment.wxml
    <!--pages/post/post-comment/post-comment.wxml-->
    <view class='comment-detail-box'>
      <view classs='comment-main-box'>
        <view class='comment-title'>评论......(共{{comments.length}}条)</view>
        <block wx:for="{{comments}}" wx:for-item="item" wx:for-index="idx">
          <view class='comment-item'>
            <view class='comment-item-header'>
              <view class='left-img'>
                <image src='{{item.avatar}}'></image>
              </view>
              <view class='right-user'>
                <text class='user-name'>{{item.username}}</text>
              </view>
            </view>
            <view class='comment-body'>
              <view class='comment-text' wx:if="{{item.content.text}}">
                <text>{{item.content.txt}}</text>
              </view>
              <view class='comment-voice' wx:if="{{item.content.audio && item.content.audio.url}}">
              <view data-url='{{item.content.audio.url}}' class='comment-voice-item' catchtap="playAudio">
              <image src='/images/icon/wx_app_voice.png' class='voice-play'></image>
              <text>{{item.content.audio.timeLen}}</text>
              </view>
              </view>
              <view class='comment-img' wx:if="{{item.content.img.length!=0}}">
              <block wx:for="{{item.content.img}}" wx:for-item="img">
              <image src='{{img}}' mode='aspectFill'></image>
              </block>
              </view>
            </view>
            <view class='comment-time'>{{item.create_time}}</view>
          </view>
        </block>
      </view>
    </view>
    
    2.8.2 添加评论列表的样式
    post-comment.wxss
    /* pages/post/post-comment/post-comment.wxss */
    
    .comment-detail-box {
      position: absolute;
      top: 0;
      left: 0;
      bottom: 0;
      right: 0;
       overflow-y: hidden; 
    }
    
    .comment-main-box {
      position: absolute;
      top: 0;
      left: 0;
      bottom: 100rpx;
      right: 0;
      overflow-y: auto;
    }
    
    .comment-title {
      height: 60rpx;
      line-height: 60rpx;
      font-size: 28rpx;
      color: #212121;
      border-bottom: 1px solid #ccc;
      margin-left: 24rpx;
      padding: 8rpx 0;
      font-family: Microsoft YaHei;
    }
    
    .comment-item {
      margin: 20rpx 0 20rpx 24rpx;
      padding: 24rpx 24rpx 24rpx 0;
      border-bottom: 1rpx solid #f2e7e1;
    }
    
    .comment-item:last-child {
      border-bottom: none;
    }
    
    .comment-item-header {
      display: flex;
      flex-direction: row;
      align-items: center;
    }
    
    .comment-item-header .left-img image {
      height: 80rpx;
      width: 80rpx;
    }
    
    .comment-item-header .right-user {
      margin-left: 30rpx;
      line-height: 80rpx;
    }
    
    .comment-item-header .right-user text {
      font-size: 26rpx;
      color: #212121;
    }
    
    .comment-body {
      font-size: 26rpx;
      line-height: 26rpx;
      color: #666;
      padding: 10rpx 0;
    }
    
    .comment-text text {
      line-height: 50rpx;
    }
    
    .comment-voice-item {
      display: flex;
      flex-direction: row;
      align-items: center;
      width: 200rpx;
      height: 64rpx;
      border: 1px solid #ccc;
      background-color: #fff;
      border-radius: 6rpx;
    }
    
    .comment-voice-item .voice-play {
      height: 64rpx;
      width: 64rpx;
    }
    
    .comment-voice-item text {
      margin-left: 60rpx;
      color: #212121;
      font-size: 22rpx;
    }
    
    .comment-img {
      margin: 10rpx 0;
    }
    
    .comment-img image {
      max-width: 32%;
      margin-right: 10rpx;
      width: 220rpx;
      height: 22orpx;
    }
    
    .comment-time {
      margin-top: 10rpx;
      color: #ccc;
      font-size: 24rpx;
    }
    
    
    image.png
    • 样式注意点:
      1)overflow-y: hidden


      image.png
    image.png

    2).comment-item:last-child


    image.png

    2.9 实现图片预览

    image.png
    2.9.1 实现图片预览
    post-comment.wxml
              <view class='comment-img' wx:if="{{item.content.img.length!=0}}">
              <block wx:for="{{item.content.img}}" wx:for-item="img" wx:for-index="imgIdx">
              <image src='{{img}}' mode='aspectFill' catchtap='previewimg' data-comment-idx='{{idx}}' data-img-idx='{{imgIdx}}'></image>
              </block>
              </view>
    
    image.png image.png image.png

    2.10 实现提交评论的界面

    2.10.1 评论框的骨架
    post-comment.wxml
     <view class="input-box">
        <view class="send-msg-box">
          <view hidden="{{useKeyboardFlag}}" class="input-item">
            <image src="/images/icon/wx_app_keyboard.png" class="comment-icon keyboard-icon" catchtap="switchInputType"></image>
            <input class="input speak-input {{recodingClass}}" value="按住 说话" disabled="disabled" catchtouchstart="recordStart" catchtouchend="recordEnd" />
          </view>
          <view hidden="{{!useKeyboardFlag}}" class="input-item">
            <image class="comment-icon speak-icon" src="/images/icon/wx_app_speak.png" catchtap="switchInputType"></image>
            <input class="input keyboard-input"  value="{{keyboardInputValue}}" bindconfirm="submitComment" bindinput="bindCommentInput" placeholder="说点什么吧……" />
          </view>
          <image class="comment-icon add-icon" src="/images/icon/wx_app_add.png" catchtap="sendMoreMsg"></image>
          <view class="submit-btn" catchtap="submitComment">发送</view>
        </view>
     </view>
    
    2.10.2 评论框的样式
    post-comment.wxss
    
    /*******************评论框**********************/
    .input-box{
        position: absolute;
        bottom: 0;
        left:0;
        right: 0;
        background-color: #EAE8E8;
        border-top:1rpx solid #D5D5D5;
        min-height: 100rpx;
        z-index: 1000;
    }
    .input-box .send-msg-box{
        width: 100%;
        height: 100%;
        display: flex;
        padding: 20rpx 0;
    }
    .input-box .send-more-box{
        margin: 20rpx 35rpx 35rpx 35rpx;
    }
    .input-box .input-item{
        margin:0 5rpx;
        flex:1;
        width: 0%;
        position: relative;
    }
    .input-box .input-item .comment-icon{
        position: absolute;
        left:5rpx;
        top:6rpx;
    }
    
    .input-box .input-item .input{
        border: 1rpx solid #D5D5D5;
        background-color: #fff;
        border-radius: 3px;
        line-height: 65rpx;
        margin:5rpx 0 5rpx 75rpx ;
        font-size: 24rpx;
        color: #838383;
        padding: 0 2%;
    }
    .input-box .input-item .keyboard-input{
        width: auto;
        max-height: 500rpx;
        height: 65rpx;
        word-break:break-all;
        overflow:auto;
    }
    .input-box .input-item .speak-input{
        text-align: center;
        color: #212121;
        height: 65rpx;
    }
    
    .input-box .input-item .recoding{
        background-color: #ccc;
    }
    
    .input-box .input-item .comment-icon.speak-icon{
        height: 62rpx;
        width: 62rpx;
    }
    .input-box .input-item .comment-icon.keyboard-icon{
        height: 60rpx;
        width: 60rpx;
        left:6rpx;
    }
    .input-box .add-icon{
        margin:0 5rpx;
        height: 65rpx;
        width: 65rpx;
        transform: scale(0.9);
        margin-top: 2px;
    }
    .input-box .submit-btn{
        font-size: 24rpx;
        margin-top: 5rpx;
        margin-right: 8rpx;
        line-height: 60rpx;
        width: 120rpx;
        height: 60rpx;
        background-color: #4A6141;
        border-radius:5rpx;
        color: #fff;
        text-align: center;
        font-family:Microsoft Yahei;
    }
    
    
    .send-more-box .more-btn-item{
        display: inline-block;
        width: 110rpx;
        height: 145rpx;
        margin-right: 35rpx;
        text-align: center;
    }
    
    .more-btn-main{
        width: 100%;
        height:60rpx;
        text-align: center;
        border:1px solid #D5D5D5;
        border-radius: 10rpx;
        background-color: #fbfbfc;
        margin: 0 auto;
        padding:25rpx 0
    }
    .more-btn-main image{
        width: 60rpx;
        height: 60rpx;
    }
    .send-more-box .more-btn-item .btn-txt{
        color: #888888;
        font-size: 24rpx;
        margin:10rpx 0;
    }
    
    .send-more-result-main{
        margin-top: 30rpx;
    }
    .send-more-result-main .file-box{
        margin-right: 14rpx;
        height: 160rpx;
        width: 160rpx;
        position: relative;
        display: inline-block;
    }
    
    .send-more-result-main .file-box.deleting{
        animation:deleting 0.5s ease;
        animation-fill-mode: forwards;
    }
    
    @keyframes deleting {
        0%{
            transform: scale(1);
        }
        100%{
            transform: scale(0);
        }
    }
    
    .send-more-result-main image{
        height: 100%;
        width: 100%;
    }
    .send-more-result-main .remove-icon{
        position: absolute;
        right: 5rpx;
        top: 5rpx;
    }
    
    .send-more-result-main .file-box .img-box {
        height: 100%;
        width: 100%;
    
    image.png

    2.11 wx:if 与 hidden控制元素显示和隐藏

    在小程序中,隐藏UI元素的方法有俩种:
    1)wx:if
    2)hidden


    image.png

    它们都是通过一个状态变量来控制元素的显示和隐藏

    • wx:if 和 hidden 有什么异同?
      1)wx:if的切换和渲染机制较为复杂,当wx:if进行切换时,MINA框架有一个局部渲染的过程,它会确保条件块在切换时销毁或者重新渲染。
      2)wx:if是惰性的,如果初始渲染条件为false,那么MINA框架什么也不做,在条件第一次变成真的时候才开始局部渲染。
      3)hidden就比较简单,组件始终会被渲染,只是简单地控制显示与隐藏
      4)一般来说,wx:if有更高的切换消耗,而hidden有更高的初始化渲染消耗。因此,在需要频繁切换的情境下用hidden更好,在运行时条件不太可能改变时用wx:if较好。

    2.12 实现文字评论框和语音评论框的切换

    2.12.1 初始化useKeyboardFlag
    post-comment.js
    image.png
    2.12.1 切换useKeyboardFlag
    post-comment.js
    image.png image.png image.png

    2.13 input组件

    属于小程序中最为重要的数据输入组件

    2.13.1 属性介绍
    image.png
    image.png
    2.13.2 事件介绍
    image.png

    由MINA框架直接指定的,属于非冒泡事件,不需要在事件名称前面再添加catch和bind

    • bindinput事件较为特殊,具有如下几个特点:
      1)当用户输入字符时触发
      2)每当用户输入或者删除一个字符时,bindinput事件都会触发一次
      3)可以在事件响应函数中使用return返回一个字符或者字符串,该字符串将替换input输入框的显示文本
      4)比较适合用来做“即时搜索”的功能

    input输入值都是在事件对应的响应函数中使用 event.detail.value来获取

    2.14 bindinput事件

    2.14.1 响应bindinput事件
    post-comment.js
      //获取用户输入
      bindCommentInput:function(event){
        var val = event.detail.value;
        console.log(val);
        this.data.keyboardInputValue = val;
      }
    
    image.png

    2.15 屏蔽评论关键字

    bindinput还有一个有意思的特性,就是在事件响应函数中可以return一个值来代替当前的输入值,并显示在input中

    2.15.1 bindinput的return值
    post-comment.js

    将这个函数的代码临时改一下,用来测试

        bindCommentInput: function (event) {
        var val = event.detail.value;
        return val + "#";
    
    image.png
    2.15.2 屏蔽关键字“qq”
    post-comment.js
     bindCommentInput: function(event) {
        var value = event.detail.value;
        var pos = event.detail.cursor;
        console.log("pos======" + pos);
        if (pos != -1) {
          //光标在中间
          var left = event.detail.value.slice(0, pos);
          console.log(left);
          //计算光标的位置
          pos = left.replace(/qq/g, "*").length;
        }
        //直接返回对象,可以对输入进行过滤处理,同时可以控制光标的位置
        return {
          value: value.replace(/qq/g, "*"),
          cursor: pos
        };
      }
    
    image.png
    2.15.3 简化版屏蔽关键字“qq”
    post-comment.js
     bindCommentInput: function(event) {
        var value = event.detail.value;
        return value.replace(/qq/g, "*");
      }
    
    
    image.png

    2.16 实现自定义发送按钮

    2.16.1 实现 submitComment 方法
    post-comment.js
    //提交用户评论
      submitComment: function(event) {
        var newData = {
          username: "青石",
          avatar: "/images/avatar/avatar-3.png",
          //评论时间
          create_time: new Date().getTime() / 1000,
          //评论内容
          content: {
            text: this.data.keyboardInputValue
          },
        };
    
        if (!newData.content.text) {
          //如果没有评论内容,就不执行任何操作
          return;
        }
    
        //保存新评论到缓存数据库中
        this.dbPost.newComment(newData);
        //显示操作结果
        this.showCommitSuccessToast();
        //重新渲染并绑定所有评论
        this.bindCommentData();
        //恢复初始状态
        this.resetAllDefaultStatus();
      },
    
    2.16.2 编写newComment方法
    DBPost.js
     //发表评论
      newComment(newComment) {
        this.updatePostData("comment", newComment);
      }
    
    
    2.16.3 在updatePostData中处理新评论
    DBPost.js
     //更新本地的点赞、评论信息、收藏、阅读量
      updatePostData(category, newComment) {
        var itemData = this.getPostItemById(),
          postData = itemData.data,
          allPostData = this.getAllPostData();
    
        switch (category) {
          case 'collect':
            //处理收藏
            if (!postData.collectionStatus) {
              //如果当前状态是未收藏
              postData.collectionNum++;
              postData.collectionStatus = true;
            } else {
              //如果当前状态是收藏
              postData.collectionNum--;
              postData.collectionStatus = false;
            }
            break;
          case 'up':
            if (!postData.upStatus) {
              postData.upNum++;
              postData.upStatus = true;
            } else {
              postData.upNum--;
              postData.upStatus = false;
            }
            break;
    
          case "comment":
            postData.comments.push(newComment);
            postData.commentNum++;
            break;
          default:
            break;
        }
    
    2.16.4 编写新增评论成功的提示方法
    post-comment.js
      //评论成功
      showCommitSuccessToast: function() {
        wx.showToast({
          title: '评论成功',
          duration: 1000,
          icon: "success"
        })
      },
    
    2.16.5 重新绑定评论数据
    post-comment.js
    bindCommentData: function() {
        var comments = this.dbPost.getCommentData();
        //绑定评论数据
        this.setData({
          comments: comments
        })
      },
    
    2.16.6 重置input组件的输入值
    post-comment.js
      //将所有相关的按钮状态、输入状态都回复到初始化状态
      resetAllDefaultStatus: function() {
        //清空评论框
        this.setData({
          keyboardInputValue: ""
        })
      }
    
    image.png

    2.17 同时支持模拟器回车、真机点击“完成”发送评论

    如果要想在模拟器中实现回车发送评论信息的功能,可以使用如下几个input事件:

    • bindchange(早期版本存在,新版本说明文档不在了,不过依然有效,只是不建议用)
    • bindblur:可以触发回车的原理是:点击回车后,input组件将失去焦点,从而触发bindblur事件
    • bindconfirm:可以在真机上响应键盘的“完成”点击事件,同时也可以在模拟器中响应键盘的“回车”事件
    2.17.1 添加bindcomfirm事件
    post-comment.wxml
    image.png

    2.18 图片与拍照评论的界面实现

    2.18.1 选择图片与拍照面板代码
    post-comment.wxml
        <view class="send-msg-box">
          <view hidden="{{useKeyboardFlag}}" class="input-item">
            <image src="/images/icon/wx_app_keyboard.png" class="comment-icon keyboard-icon" catchtap="switchInputType"></image>
            <input class="input speak-input {{recodingClass}}" value="按住 说话" disabled="disabled" catchtouchstart="recordStart" catchtouchend="recordEnd" />
          </view>
          <view hidden="{{!useKeyboardFlag}}" class="input-item">
            <image class="comment-icon speak-icon" src="/images/icon/wx_app_speak.png" catchtap="switchInputType"></image>
            <input class="input keyboard-input"  value="{{keyboardInputValue}}" bindconfirm="submitComment" bindinput="bindCommentInput" placeholder="说点什么吧……" />
          </view>
          <image class="comment-icon add-icon" src="/images/icon/wx_app_add.png" catchtap="sendMoreMsg"></image>
          <view class="submit-btn" catchtap="submitComment">发送</view>
        </view>
    
         <view class="send-more-box" hidden="{{!sendMoreMsgFlag}}">
    
          <!--选择图片和拍照的按钮-->
          <view class="send-more-btns-main">
            <view class="more-btn-item" catchtap="chooseImage" data-category="album">
              <view class="more-btn-main">
                <image src="/images/icon/wx_app_upload_image.png"></image>
              </view>
              <text>照片</text>
            </view>
            <view class="more-btn-item" catchtap="chooseImage" data-category="camera">
              <view class="more-btn-main">
                <image src="/images/icon/wx_app_camera.png"></image>
              </view>
              <text>拍照</text>
            </view>
          </view>
    
          <!--显示选择的图片-->
          <view class="send-more-result-main" hidden="{{chooseFiles.length==0}}">
            <block wx:for="{{chooseFiles}}" wx:for-index="idx">
    
              <!--如果删除其中一个,则对其添加deleting 样式;-->
              <view class="file-box {{deleteIndex==idx?'deleting':''}}">
                <view class="img-box">
                  <image src="{{item}}" mode="aspectFill"></image>
                  <icon class="remove-icon" type="cancel" size="23" color="#B2B2B2" catchtap="deleteImage" data-idx="{{idx}}" />
                </view>
              </view>
            </block>
          </view>
        </view>
     </view>
    </view>
    
    
    2.18.2 编写sendMoreMsg方法
    image.png
    post-comment.js
    image.png
    sendMoreMsg:function(){
        this.setData({
          sendMoreMsgFlag: !this.data.sendMoreMsgFlag
        })
      }
    
    image.png

    2.19 实现从相册选择照片与拍照

    2.19.1 新增chooseFiles变量
    post-comment.js
      /**
       * 页面的初始数据
       */
      data: {
        //控制使用键盘还是发送语音
        useKeyboardFlag: true,
        //控制input组件的初始值
        keyboardInputValue:"",
        //控制是否显示图片选择面板
        sendMoreMsgFlag:false,
        //保存已经选择的图片
        chooseFiles:[]
      },
    
    2.19.2 实现选择图片与拍照功能
    post-comment.js
    //选择本地照片与拍照
      chooseImage: function(event) {
        //已选择图片数组
        var imgArr = this.data.chooseFiles;
        //只能上传3张照片,包括拍照
        var leftCount = 3 - imgArr.length;
        if (leftCount <= 0) {
          return;
        }
        var sourceType = [event.currentTarget.dataset.category],
          that = this;
        console.log("sourceType==============" + sourceType);
        console.log("that==============" + that);
        wx.chooseImage({
          count: leftCount,
          sourceType: sourceType,
          success: function(res) {
            that.setData({
              chooseFiles: imgArr.concat(res.tempFilePaths)
            })
          },
        })
      }
    
    image.png image.png

    注意,sourceType的传值过程

    image.png

    2.20 icon图片

    image.png
    image.png

    2.21 删除已选择的图片

    2.21.1 deleteImage方法
    post-comment.js
      //删除已经选择的图片
      deleteImage: function(event) {
        var index = event.currentTarget.dataset.idx;
        var that = this;
        that.data.chooseFiles.splice(index, 1);
        that.setData({
          chooseFiles: that.data.chooseFiles
        })
      }
    

    获取当前删除图片的序号,并且将该图片的URL从 this.data.chooseFiles数组中删除,重新绑定chooseFiles变量即可

    2.22 在小程序中使用CSS 3动画

    image.png

    如果delete的值等于当前图片的序号,就说明该图片是要被删除的,需要添加一个deleteing动画


    image.png
    2.22.1 修改deleteImage方法
    post-comment.js
    image.png
     //删除已经选择的图片
      deleteImage: function(event) {
        var index = event.currentTarget.dataset.idx;
        var that = this;
        that.setData({
          deleteIndex:index
        });
        that.data.chooseFiles.splice(index, 1);
        setTimeout(function(){
          that.setData({
            deleteIndex:-1,
            chooseFiles: that.data.chooseFiles
          })
        },1000)
      }
    
    image.png

    2.23 实现图片评论的发送

    实现原理:把当前的this.data.chooseFiles所保存的图片地址存入数据库缓存中,并且重新渲染评论列表即可。

    修改如下的方法

    2.23.1 修改submitComment方法
    post-comment.js
    //提交用户评论
      submitComment: function(event) {
        var imgs = this.data.chooseFiles;
        var newData = {
          username: "青石",
          avatar: "/images/avatar/avatar-3.png",
          //评论时间
          create_time: new Date().getTime() / 1000,
          //评论内容
          content: {
            text: this.data.keyboardInputValue,
            img:imgs
          },
        };
    
        if (!newData.content.text && imgs.length == 0) {
          //如果没有评论内容,就不执行任何操作
          return;
        }
    
        //保存新评论到缓存数据库中
        this.dbPost.newComment(newData);
        //显示操作结果
        this.showCommitSuccessToast();
        //重新渲染并绑定所有评论
        this.bindCommentData();
        //恢复初始状态
        this.resetAllDefaultStatus();
      },
    
    2.23.2 修改submitComment方法
    post-comment.js
      //将所有相关的按钮状态、输入状态都回复到初始化状态
      resetAllDefaultStatus: function() {
        //清空评论框
        this.setData({
          keyboardInputValue: "",
          chooseFiles:[],
          sendMoreMsgFlag:false
        })
      },
    
    image.png

    2.24 实现语音信息的发送

    • 发送语音评论的操作过程:
      1)切换到语音面板
      2)长按“按住说话”这个按钮
      3)说话
      4)松开“按住说话”,语音信息自动发送

    [图片上传中...(image.png-a8f196-1529334605160-0)]

    2.24.1 实现recodrdStart
    post-comment.js
    //开始录音
      recordStart: function() {
        var that = this;
        this.setData({
          recodingClass: "recoding"
        });
        //记录录音开始时间
        this.startTime = new Date();
        console.log("this.startTime============" + this.startTime);
        wx.startRecord({
          success: function(res) {
            //计算录音时长
            var diff = (that.endTime - that.startTime) / 1000;
            console.log("diff============" + diff);
            diff = Math.ceil(diff);
            console.log("diff======ceil======" + diff);
    
            //发送录音
            that.submitVoiceComment({
              url: res.tempFilePath,
              timeLen: diff
            });
          },
    
          fail: function(res) {
            console.log("fail============" + res);
          },
          complete: function(res) {
            console.log("complete============" + res);
          }
        })
      },
    
    2.24.2 结束录音
    post-comment.js
     //结束录音
      recordEnd: function() {
        this.setData({
          recodingClass: ""
        });
        this.endTime = new Date();
        wx.stopRecord();
      },
    
    2.24.3 发送语音评论
    post-comment.js
    //提交录音
      submitVoiceComment: function (audio){
        var newData = {
          username: "青石",
          avatar: "/images/avatar/avatar-3.png",
          //评论时间
          create_time: new Date().getTime() / 1000,
          //评论内容
          content: {
            text: "",
            img: [],
            audio:audio
          },
        };
        console.log("submitVoiceComment====create_time=========" + newData.create_time);
    
        //保存新评论到缓存数据中
        this.dbPost.newComment(newData);
    
        //显示操作结果
        this.showCommitSuccessToast();
    
        //重新渲染并且绑定所有评论
        this.bindCommentData();
      }
    
    image.png image.png

    2.25 实现语音信息的暂停与播放

    语音评论的播放需要满足以下几个播放场景。假设有两个信息------A语音和B语音,当点击A语音时:

    • 如果A语音处于未播状态,就开始播放A语音
    • 如果A语音处于暂停状态,就继续播放A语音
      当点击B语音时:
    • B语音的行为同上述A语音
    • 无论A语音处于何种状态,都将立刻被中断;被中断后,再次点击A语音,A语音重新开始播放
    2.25.1 发送语音评论
    post-comment.js
    playAudio: function(event) {
        console.log("playAudio=============");
        var url = event.currentTarget.dataset.url,
          that = this;
        console.log("url=============" + url);
        //暂停当前录音
        if (url == this.data.currentAudio) {
          wx.pauseVoice();
          this.data.currentAudio = ""
    
          //播放录音
        } else {
          this.data.currentAudio = url;
          wx.playVoice({
            filePath: url,
            complete: function() {
              //只有当录音播放完毕后才会执行
              that.data.currentAudio = ""
            }
          })
        }
      }
    
    2.25.2 定义currentAudio变量
    post-comment.js
      data: {
        //控制使用键盘还是发送语音
        useKeyboardFlag: true,
        //控制input组件的初始值
        keyboardInputValue: "",
        //控制是否显示图片选择面板
        sendMoreMsgFlag: false,
        //保存已经选择的图片
        chooseFiles: [],
        //被删除的图片序号
        deleteIndex: -1,
        //保存当前正在播放语音的URL
        currentAudio: ''
      },
    
    image.png

    2.26 用户授权

    使用某些特定的API,是需要用户主动授权的,比如,调用wx.startRecord接口录音时,需要用户主动授权,如下所示:


    image.png

    如果用户点击确定后,以后使用该功能的时候都不会提示,如果要让弹出授权框,需要手动清除授权文件,如下所示:


    image.png

    2.27 解决真机运行时评论页面滑动卡顿的问题

    image.png

    2.28 文章阅读计算功能

    2.28.1 阅读数+1
    post-detail.js
    //阅读量+1
      addReadingTimes:function(){
        this.dbPost.addReadingTimes();
      }
    
    2.28.2 阅读数+1的缓存操作方法
    DBPost.js
      //阅读量+1
      addReadingTimes() {
        this.updatePostData("reading");
      }
    
    2.28.3 updatePostData方法最终代码
    DBPost.js
     //更新本地的点赞、评论信息、收藏、阅读量
      updatePostData(category, newComment) {
        var itemData = this.getPostItemById(),
          postData = itemData.data,
          allPostData = this.getAllPostData();
    
        switch (category) {
          case 'collect':
            //处理收藏
            if (!postData.collectionStatus) {
              //如果当前状态是未收藏
              postData.collectionNum++;
              postData.collectionStatus = true;
            } else {
              //如果当前状态是收藏
              postData.collectionNum--;
              postData.collectionStatus = false;
            }
            break;
          case 'up':
            if (!postData.upStatus) {
              postData.upNum++;
              postData.upStatus = true;
            } else {
              postData.upNum--;
              postData.upStatus = false;
            }
            break;
    
          case "comment":
            postData.comments.push(newComment);
            postData.commentNum++;
            break;
    
          case "reading":
            postData.readingNum++;
            break;
          default:
            break;
        }
    
    2.28.4 调用addReadingTimes方法
    post-detail.js
      /**
       * 生命周期函数--监听页面加载
       */
      onLoad: function (options) {
        var postId = options.id;
        this.dbPost = new DBPost(postId);
        this.postData = this.dbPost.getPostItemById().data;
        this.setData({
          post:this.postData
        })
    
        this.addReadingTimes();
      },
    

    每次进来阅读量都会加1


    image.png image.png

    相关文章

      网友评论

        本文标题:1.6 收藏、评论、点赞、计数功能准备工作

        本文链接:https://www.haomeiwen.com/subject/hdlgeftx.html