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);
}
- 属性注意点
1)transform: scale()属性
2)display: inline-block;属性
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>
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来帮助开发者处理交互相关的问题,目前主要有如下几个:
2.3.1 文章收藏功能的交互反馈
post-detail.js
wx.showToast({
title: newData.collectionStatus? '收藏成功':'取消成功',
duration:1000,
icon:'success',
mask:true
})
-参数说明
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>
2.4 本地缓存的重要性及应用举例
提供本地的key&value缓存机制是小程序的一大特点,善用本地缓存可以极大地改善客户端的体验与服务器的性能。
在一个高性能的产品中,缓存的重要性是不言而喻的,建议开发者将本地缓存视作为一个本地的key&value数据库,并封装一些类和公用方法,提供给项目中的各个调用方,最好不要让getStorage 、setStorage等方法充斥在项目的每一个角落
2.5 支持文字、图片、拍照、语音上传的文章评论
2.5.1 注册post-comment页面
app.json
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')
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;
}
-
样式注意点:
1)overflow-y: hidden
2).comment-item:last-child
2.9 实现图片预览
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>
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%;
2.11 wx:if 与 hidden控制元素显示和隐藏
在小程序中,隐藏UI元素的方法有俩种:
1)wx:if
2)hidden
它们都是通过一个状态变量来控制元素的显示和隐藏
- 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
2.12.1 切换useKeyboardFlag
post-comment.js
2.13 input组件
属于小程序中最为重要的数据输入组件
2.13.1 属性介绍
2.13.2 事件介绍
由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;
}
2.15 屏蔽评论关键字
bindinput还有一个有意思的特性,就是在事件响应函数中可以return一个值来代替当前的输入值,并显示在input中
2.15.1 bindinput的return值
post-comment.js
将这个函数的代码临时改一下,用来测试
bindCommentInput: function (event) {
var val = event.detail.value;
return val + "#";
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
};
}
2.15.3 简化版屏蔽关键字“qq”
post-comment.js
bindCommentInput: function(event) {
var value = event.detail.value;
return value.replace(/qq/g, "*");
}
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: ""
})
}
2.17 同时支持模拟器回车、真机点击“完成”发送评论
如果要想在模拟器中实现回车发送评论信息的功能,可以使用如下几个input事件:
- bindchange(早期版本存在,新版本说明文档不在了,不过依然有效,只是不建议用)
- bindblur:可以触发回车的原理是:点击回车后,input组件将失去焦点,从而触发bindblur事件
- bindconfirm:可以在真机上响应键盘的“完成”点击事件,同时也可以在模拟器中响应键盘的“回车”事件
2.17.1 添加bindcomfirm事件
post-comment.wxml
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方法
post-comment.js
sendMoreMsg:function(){
this.setData({
sendMoreMsgFlag: !this.data.sendMoreMsgFlag
})
}
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)
})
},
})
}
注意,sourceType的传值过程
2.20 icon图片
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动画
如果delete的值等于当前图片的序号,就说明该图片是要被删除的,需要添加一个deleteing动画
2.22.1 修改deleteImage方法
post-comment.js
//删除已经选择的图片
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)
}
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
})
},
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();
}
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: ''
},
2.26 用户授权
使用某些特定的API,是需要用户主动授权的,比如,调用wx.startRecord接口录音时,需要用户主动授权,如下所示:
如果用户点击确定后,以后使用该功能的时候都不会提示,如果要让弹出授权框,需要手动清除授权文件,如下所示:
2.27 解决真机运行时评论页面滑动卡顿的问题
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