打怪升级小程序评论回复和发贴功能实战一

9次阅读

共计 6396 个字符,预计需要花费 16 分钟才能阅读完成。

在学习成长的过程中,常常会遇到一些自己从未接触的事物,这就好比是 打怪升级 ,每次打倒一只怪,都会获得经验,让自己进步强大。特别是我们这些做技术的, 逆水行舟不进则退 。下面分享下小程序开发中的 打怪升级 经历~

先来看下实际效果图,小程序开发中有时会要做一些的功能复杂的组件,比如评论回复和发帖功能等,这次主要讲的是关于 评论模块 的一些思路和实战中的经验,希望能抛砖引玉,给大家一些启发,一同成长~

>>(最下面有实战 demo 的地址,可以直接浏览器打开添加至 IDE 工具中) <<

根据这个 demo.gif,本人做了一个简单的流程图,帮助大家理解。下面罗列一些开发中需要“打的怪”:

1、组件目录结构

├─components      --- 小程序自定义组件
│  ├─plugins      ---(重点)可独立运行的大型模块,可以打包成 plugins
│  │  ├─comment         --- 评论模块
│  │  │  │  index.js
│  │  │  │  index.json
│  │  │  │  index.wxml
│  │  │  │  index.wxss
│  │  │  │  services.js    ---(重点)用来处理和清洗数据的 service.js,配套模板和插件
         │      
         └─submit    --- 评论模块子模块:提交评论
                 index.js
                 index.json
                 index.wxml
                 index.wxss

为什么要单独做个 评论页面页面(submit)
因为如果是当前页面最下面 input 输入的形式,会出现一些兼容问题,比如:

  • 不同手机的虚拟键盘高度不同,不好 绝对定位 和完全适配
  • 弹窗输入框过小输入不方便,如果是大的 textare 时,容易误触下面评论的交。

注:目录结构,仅供参考。

2、NODE 端 API 接口返回结构和页面结构

//node:API 接口返回
{
    "data": {
        "commentTotal": 40,
        "comments": [
            {
                "contentText": "喜欢就关注我",   // 评论内容
                "createTime": 1560158823647,    // 评论时间
                "displayName": "智酷方程式",       // 用户名
                "headPortrait": "https://blz.nosdn.127.net/1/weixin/zxts.jpg",  // 用户头像
                "id": "46e0fb0066666666",  // 评论 ID  用于回复和举报
                "likeTotal": 2,    // 点赞数
                "replyContents": [   // 回复评论
                    {
                        "contentText": "@智酷方程式  喜欢就回复我",   // 回复评论内容
                        "createTime": 1560158986524,   // 回复时间
                        "displayName": "神秘的前端开发",   // 回复的用户名
                        "headPortrait": "https://blz.nosdn.127.net/1/2018cosplay/fourth/tesss.jpg",  // 回复的用户头像
                        "id": "46e0fb00111111111",   // 回复评论的 ID
                        "likeTotal": 2,    // 回复评论的点赞数
                        "replyContents": [],   // 回复的回复 盖楼
                        "replyId": "46e0fb001ec222222222",   // 回复评论的独立 ID,用于统计
                    },
                    {
                        "contentText": "@智酷方程式:威武,学习学习",
                        "createTime": 1560407232814,
                        "displayName": "神秘的前端开发",
                        "headPortrait": "https://blz.nosdn.127.net/1/2018cosplay/fourth/tesss.jpg",
                        "id": "46e0fb00111111111",
                        "likeTotal": 0,
                        "replyContents": [],
                        "replyId": "46e0fb001ec222222222",
                    }
                ],
                "replyId": "","topicId":"46e0fb001ec3333333",
            }
        ],
        "curPage": 1,  // 当前页面
        // 通过 ID 判断  当前用户点赞了 哪些评论
        "likes": [
            "46e0fb00111111111",    
            "46e0fb001ec222222222",
            "46e0fb0066666666",
        ],
        "nextPage": null, // 下一页
        "pageSize": 20,  // 一页总共多少评论
        "total": 7,   // 总共多少页面
    },
    "msg": "success",
    "status": "success"
}
<!-- HTML 部分 -->
<block wx:if="{{commentList.length>0}}">
    <!-- 评论模块 -->
    <block wx:for="{{commentList}}" wx:for-item="item" wx:for-index="index" wx:key="idx">
        <view class="commentItem" catchtap="_goToReply" data-contentid="{{item.id}}" data-replyid="{{item.id}}"
            data-battle-tag="{{item.displayName}}">
            <view class="titleWrap">
                <image class="logo" src="{{item.headPortrait||' 默认图 '}}"></image>
                <view class="authorWrap">
                    <view class="author">{{item.displayName}}</view>
                    <view class="time">{{item.createTime}}</view>
                </view>
                <view class="starWrap" catchtap="_clickLike" data-index="{{index}}" data-like="{{item.like}}"
                    data-contentid="{{item.id}}" data-topicid="{{item.topicId}}">
                    <text class="count">{{item.likeTotal||""}}</text>
                    <view class="workSprite icon {{item.like?'starIconHasClick':'starIcon'}}"></view>
                </view>
            </view>
            <view class="text">
                {{item.contentText}}
            </view>
        </view>
        <!-- 评论的评论 -->
        <block wx:for="{{item.replyContents}}" wx:for-item="itemReply" wx:for-index="indexReply" wx:key="idxReply">
            <view class="commentItem commentItemReply" catchtap="_goToReply" data-contentid="{{itemReply.id}}"
                data-replyid="{{item.id}}" data-battle-tag="{{itemReply.displayName}}">
                ... 和上面类似
            </view>
        </block>
    </block>
    <!-- 加载更多 loading -->
    <block wx:if="{{isOver}}">
        <view class="more"> 评论加载完成 </view>
    </block>
</block>

通过 node 提供一个 API 接口,通过用户的 openId 来判断是否点赞,这里提供一个 参考的 JSON结构。
JSON 尽量做成 array 循环的结构方便渲染,根据 ID 来 BAN 人和管理。底部加上加载更多的效果,同时,记得做一些兼容,比如默认头像等。

3、评论中的一些微信原生交互

这里建议很多交互如果不是必须要特别定制,可以才用微信原生的组件,效果和兼容性都有保障,而且方便简单。

对评论进行回复 / 举报

<!-- HTML 部分 通过绑定事件:_goToReply 进行交互 -->
<view class="commentItem" catchtap="_goToReply" data-contentid="{{item.id}}" data-replyid="{{item.id}}"
    data-battle-tag="{{item.displayName}}">
    ... 内部省略
</view>
//JS 部分  微信原生 wx.showActionSheet 显示操作菜单交互
_goToReply(e) {
    //  上面的各种授权判断省略...
    let self = this;
    wx.showActionSheet({itemList: ['回复', '举报'],
        success: function (res) {if (!res.cancel) {console.log(res.tapIndex);
                // 前往评论
                if (res.tapIndex == 0) {
                    // 判断是否是 评论的评论
                    self._goToComment(replyid);
                }
                // 举报按钮
                if (res.tapIndex == 1) {
                    // 弹出框
                    self.setComplain(contentid);
                }
            } else {// 取消选择}
        },
        fail(res) {console.log(res.errMsg)
        }
    });
}
// 当选择“举报”的时候,二次调用 wx.showActionSheet 方法
setComplain(contentid){let complainJson = ["敏感信息", "色情淫秽", "垃圾广告", "语言辱骂", "其它"];
    wx.showActionSheet({
        itemList: complainJson,
        success: async res => {if (!res.cancel) {
                // 选择好后,提交举报
                try {let complainResult = await request.postComplainReport(complainJson[index], openid, contentid);
                    if (complainResult.msg == "success") {// 提交成功后反馈} else {}} catch (e) {console.log(e)
                }
            }
        }
    });
}

显示操作菜单 wx.showActionSheet 方法说明

属性 类型 说明
itemList Array.<string> 按钮的文字数组,数组长度最大为 6
itemColor string 按钮的文字颜色
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)

使用这个方法,即是主流的做法,也能很好的兼容不同机型,同时给予用户“习惯性体验”。

原生评论排序切换

<!-- picker 组件  html 部分 -->
<picker bindchange="bindPickerChange" value="{{index}}" range="{{array}}">
    <view class="picker">
        当前选择:{{array[index]}}
    </view>
</picker>
// js 部分
Page({
    data:{
        // 查看评论类型切换
        array: ["最佳", "最新", "只看自己"],
        // 选择数组中的第几个显示
        index:0
    },
    bindPickerChange(e) {console.log('picker 发送选择改变,携带值为', e.detail.value)
        this.setData({index: e.detail.value})
    }
})

picker 组件是一个从底部弹起的滚动选择器,这里我们用它来切换不同评论的排序。每次切换都可以通过 bindchange 获得对应的变化,通过 e.detail.value 获取用户选择的索引值。
官方文档:
https://developers.weixin.qq….

4、传参跳转写评论页

let uriData = {
    logo: "xxx.jpg",
    type: "commentReply",
    title: "文章:小程序评论,动态发帖开发指北 \n 作者:智酷方程式",
    openId:"xxxxxxxxxxx",
    replyId:"aaaaaa"   // 用户回复的是哪个评论的 ID
};
wx.navigateTo({url: `/components/plugins/comment/submit/index?uriData=${encodeURIComponent(JSON.stringify(uriData))}` });

这个可以用 encodeURIComponent 的方式处理下参数中的 中文,避免跳转发布评论页接收数据时出现乱码。

5、发表评论页

显示和控制评论的字数

<!-- html 部分  关于 textarea 的配置 -->
<view class='feedback-cont'>
    <textarea auto-focus="true" value="{{replyName}}" maxlength="200" bindinput="textareaCtrl"
        placeholder-style="color:#999;" placeholder="留下评论,共同学习,一起进步" />
    <view class='fontNum'>{{content.length}}/200</view>
</view>
<view class='feedback-btn' bindtap='commentSubmit'> 提交 </view>
// js 部分
Page({
    data: {
        // 初始化评论内容,如果是回复则通过传参变成 @xxxx 的形式
        content: "@xxxx",
    },
    textareaCtrl: function (e) {if (e.detail.value) {
            this.setData({content: e.detail.value})
        } else {
            this.setData({content: ""})
        }
    }
})

textarea 在小程序中改动不大,这个标签原有的一些属性都可以继续使用,通过配置 maxlength 来控制字数,同时,设置 auto-focus=”true” 可以让用户进到这个发表评论页面时自动弹出虚拟键盘和光标定位在输入的区域。

当然,也可以将 发表评论 评论展示区域 做在一起,这个就要考虑到要么通过“小程序 API”获取键盘高度,要么将“发布评论”置顶区域显示,也是可以做的,只是相对考虑的点会多些。当时开发评论组件的时候,考虑开发时间短和用户体验,权衡后,最终决定以上方案,希望能给到大家一些参考和借鉴,在其他组件开发中触类旁通。

[代码片段]评论回复组件实战 demo

demo 的微信路径:https://developers.weixin.qq….

demo 的 ID:oHs5cMma7N9W

如果你装了 IDE 工具,可以直接访问上面的 demo 路径

通过代码片段将 demo 的 ID 输入进去也可添加:


总结,“组件化思想 ”对于无论做小程序、react/VUE 还是其他项目来说, 减少重复开发,提高复用性 都是一个非常重要的点。评论功能 其实只要理清楚整体思路,做起来难度并不大,通过一些 原生组件 ,可以大大提高开发效率,同时保证良好的兼容性。
后面一期还将分享下功能点较多的 发帖组件 开发。

往期回顾:
[[填坑手册]小程序 Canvas 生成海报(一)](https://juejin.im/post/5ce606…
[[拆弹时刻]小程序 Canvas 生成海报(二)](https://juejin.im/post/5cee3b…
[[填坑手册]小程序目录结构和 component 组件使用心得](https://juejin.im/post/5d09e0…

正文完
 0