前言
- 学习了一阵子微信小程序云开发,为了巩固所学的知识和提高实战经验,决定与同学一起合作整一款小程序。这里主要分享一下我负责的部分和一些的学习过程,希望对您有所帮助,新人小白一枚,各方面都有很多不足之处,请多指教(〜 ̄▽ ̄)〜
效果图
- 先来看看部分效果图吧 ٩(๑^o^๑)۶
- 个人中心效果图
- 个人空间效果图
- 好友列表效果图(可实现取消关注,加关注,分组功能)
- 好友模糊搜索效果
开始前准备
- VScode代码编辑器
- 微信开发者工具
- 有赞vant组件库
- iconfont图标库
说明
- 该项目基于小程序云开发,使用的模板是云开发快速启动模板,开发过程使用了组件,云函数,数据库等相关知识,该项目所有数据均存在云端数据库中
注:数据权限要选择所有用户可读,仅创建者可读写 (´△`)
总体架构
-- cloudfunctions 云函数
remove 删除
-- commponent 自定义组件
list 默认分组
special 特别关注
-- images
icon 图标
-- pages
mine 个人中心
mySpace 我的空间
myFriend 我的关注
friendSearch 搜索好友
云函数
云函数简单来说就是在云后端(Node.js)运行的代码,本地看不到这些代码的执行过程,全封闭式只暴露接口供本地调用执行,本地只需等待云端代码执行完毕后返回结果。这也是面向接口编程的思想体现。
remove
云函数
- 在我的好友页面取消关注时需要调用
remove
云函数
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db=cloud.database()
const defaultList=db.collection('defaultList')
const specialConcern=db.collection('specialConcern')
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
const _id=event._id //获取要取消关注传入的up主的 _id
const dl=await defaultList.doc(_id).remove() //从数据库集合中查找出并删除
const sc=await specialConcern.doc(_id).remove()
return { //返回删除后的结果
dl,
sc
}
}
自定义组件
该组件会在我的好友页面用到,我是在commponent文件夹下新建自定义组件list和special,并且实现点击关注按钮弹出操作菜单,可取消关注,加关注,分组这些功能
- special组件的wxml部分(list组件同理)
xml部分
<view class="special" >
<view class="special_hd">
<view class="avatar">
<image src="{{specialConcern.avatar}}"/>
</view>
</view>
<view class="special_bd">
<text class="up">{{specialConcern.up}}</text>
<span class="digest">{{specialConcern.digest}}</span>
</view>
<view class="special_ft" bindtap="getInfo">
<button wx:if="{{tapIndex==1}}" class="concern" bindtap="concern">+ 关注</button>
<button wx:else class="areadyconcern" bindtap="cancleConcern">特别关注</button>
</view>
</view>
获取信息
//获取up主的相关信息
getInfo(){
this.triggerEvent('getInfo',{
avatar:this.data.specialConcern.avatar,
up:this.data.specialConcern.up,
digest:this.data.specialConcern.digest
})
}
操作菜单
- 点击已关注按钮弹出操作菜单,自定义组件触发事件时,使用
triggerEvent
方法,指定事件名、detail对象和事件选项
cancleConcern(){
//弹出操作菜单,选择取消或移组
wx.showActionSheet({
itemList:['默认分组','取消关注'],
success:res=>{
console.log(res);
this.setData({
tapIndex:res.tapIndex //选择默认分组,tapIndex为0;取消关注,tapIndex为1
})
// 自定义触发事件 cancle
this.triggerEvent("cancle",{
_id:this.data.specialConcern._id,
tapIndex:this.data.tapIndex
})
}
})
},
//重新关注
concern(){
this.setData({
tapIndex:2 //让 +关注 按钮隐藏,已关注按钮出现
})
// 自定义触发事件 concern
this.triggerEvent("concern",{
_id:this.data.specialConcern._id,
tapIndex:this.data.tapIndex
})
}
个人中心页
授权登录
- 个人中心页主要重点就是登录操作,所以我在这里只讲登录方面
- 由于考虑到用户信息多个页面都将用到,为了方便,我将授权获得的信息存入全局,在app.js中做以下操作
globalData : {},
onLaunch: function () {
const userInfo=wx.getStorageSync('userInfo')
if(userInfo){
this.globalData.userInfo=userInfo
}
- 登录前页面内容空白,头像为默认头像,授权后获取信息登录,内容展开,使用
hasUserInfo
判断是否为授权且为登录状态
<block wx:if="{{canIUse && !hasUserInfo}}">
<image src="../../images/icons/defaultTx.png" mode="aspectFill" class="owner-avatar"/>
<button class="owner-name" open-type="getUserInfo" bindgetuserinfo="getUserInfo">点击登录</button>
</block>
<block wx:else>
<image class="owner-avatar" src="{{userInfo.avatarUrl}}" mode="aspectFill"></image>
<text class="owner-name">{{userInfo.nickName}}</text>
</block>
data: {
canIUse: wx.canIUse('button.open-type.getUserInfo'), //判断小程序的API是否在当前版本可用
hasUserInfo:false,
userInfo: {},
},
// 调用用户信息
async getUserInfo(e){
wx.setStorageSync('userInfo', e.detail.userInfo)
app.globalData.userInfo = e.detail.userInfo //将信息放入全局app.js
if(app.globalData.userInfo){ //这里是为了判断是否授权成功
this.setData({
userInfo:e.detail.userInfo,
hasUserInfo:true,
canIUseGetUserProfile:true
})
}
},
//记住授权登录,防止下次使用时又要重新登录
async onLoad(options) {
const userInfo=app.globalData.userInfo
if(userInfo){
this.setData({
hasUserInfo:true,
userInfo:userInfo,
})
}
},
我的空间页
页面布局
- 整体页面布局简单,多采用flex布局
- 关于背景图片,自定义头部导航栏,只需在json中添加一条代码即可
"navigationStyle": "custom"
- 关于导航栏和滑块,
current
表示当前滑动的页面,index
表示导航栏索引,使用它们让导航栏和页面联动,页面的html其他布局就不介绍了。
<!-- 导航栏 -->
<view class="nav">
<view class="nav-content {{index==current?'active-nav':''}}" wx:for="{{navList}}" wx:key='{{index}}' data-index="{{index}}" bindtap="currentNav">
<text>{{item.title}}</text>
</view>
</view>
</view>
<!-- swiper -->
<swiper style="height:{{currentHeight}}px;background-color: white;" current="{{current}}" bindchange="currentPage">
// 当前滑块页
currentPage(e){
this.setData({
current:e.detail.current
})
},
// 当前选中导航栏
currentNav(e){
this.setData({
index:e.currentTarget.dataset.index,
current:e.currentTarget.dataset.index
})
},
我的好友页
导航栏
- 页面用swiper将关注和粉丝做成两个滑块,可以来回滑动,点击导航栏也可切换,实现导航栏和滑块联动
wxml
<!-- 导航栏 -->
<view class="nav" >
<view class="concern-fans {{index==current?'active-nav':''}}" wx:for="{{concern_fans}}" wx:key="index" data-index="{{index}}"
bindtap="currentNav">{{item.title}}</view>
</view>
<!-- 滑块内容 -->
<view class="swiper-content">
<swiper style="height: 100vh;" current="{{current}}" bindchange="currentPage">
js
//当前选中的滑块
currentPage(e){
this.setData({
current:e.detail.current
})
},
//当前选中的导航栏
currentNav(e){
this.setData({
index:e.currentTarget.dataset.index,
current:e.currentTarget.dataset.index
})
组件声明
- 搜索栏使用了vant组件库需声明,关注列表的自定义组件也需声明(在json中声明)
//myFriend.json
"usingComponents": {
"list":"/components/list/index",
"special":"/components/special/index",
"van-search":"../../miniprogram_npm/@vant/weapp/search/index"
},
搜索栏
- 搜索部分我是直接引入van-search组件,这里详情可查看 vant组件库,真正的搜索不需要在这个页面实现搜索功能,真正的搜索会单独建一个页面完成,它只需要完成跳转即可。所以我单独建了一个搜索页面
friendSearch
放在pages文件夹下,在下面的搜索页面中会详细讲解。
<!-- 搜索栏 -->
<navigator url="../friendSearch/friendSearch">
<van-search focus="false" shape="round" placeholder="搜索我的关注">
</van-search>
</navigator>
关注列表栏
- 列表标题栏,此处唯一比较细节的就是通过改变
specialisShow
的布尔值来控制列表栏的展开与否。
//myFriend.wxml
<!-- 关注列表 -->
<view class="content">
<view class="concern" bindtap="openClose1">
<image wx:if="{{specialisShow}}" class="arrows" src="../../images/icons/downarrow.png"/>
<image wx:else class="arrows" src="../../images/icons/uparrow.png"/>
<text class="text">特别关注</text>
<span class="count">{{specialCount}}</span>
</view>
//myFriend.js
openClose1(e){ //列表是否展开
const specialisShow=this.data.specialisShow
if(specialisShow==true){
this.setData({
specialisShow:false,
})
}else {
this.setData({
specialisShow:true,
})
}
}
- 关注列表栏,使用了自定义组件special(自定义组件在前面组件目录中有详细介绍)在myFriend.js中绑定了在组件方法中自定义的触发事件
getInfo
,cancle
,concern
,并传值
//myFriend.wxml
<!-- 关注组件 -->
<block wx:for="{{specialConcern}}" wx:key="index" wx:if="{{specialisShow}}">
<special id="special" specialConcern="{{item}}" bind:getInfo="getInfo" bind:cancle="specialCancle" bind:concern="specialConcern"></special>
</block>
//myFriend.js
getInfo(e){ //将选中的up主信息缓存
wx.setStorageSync('info',e.detail)
},
specialConcern(){ //重新关注
let info=wx.getStorageSync('info') //获取选中up主信息
specialConcern
.add({
data:{
up:info.up,
digest:info.digest,
avatar:info.avatar
}
})
}
specialCancle(event){
let tapIndex=event.detail.tapIndex
let _id=event.detail._id
if(tapIndex==1){ //tapIndex=1则表示操作菜单选中取消关注
wx.cloud.callFunction({ //调用remove云函数实现取消关注功能
name:'remove',
data:{
_id:_id
}
})
}
else if(tapIndex==0){ //tapIndex=0表示选中移分组
let info=wx.getStorageSync('info') //获取信息并在目标分组添加信息
defaultList
.add({
data:{
up:info.up,
digest:info.digest,
avatar:info.avatar
}
})
wx.cloud.callFunction({ //在目标分组添加信息后,删除原先分组中该选中的信息
name:'remove',
data:{
_id:_id
}
})
}
},
搜索页面
- 该页面是通过点击好友页面中的搜索栏跳转的真正搜索页面,搜索功能将在这个页面实现。
//搜索框
<van-search value="{{value}}"
shape="round"
focus="true"
placeholder="搜索我的关注"
action-text
use-action-slot
bind:change="onChage"
bind:search="onSearch">
<view slot="action" bind:tap="onClick">搜索</view>
</van-search>
//搜索成功后的结果
<block wx:for="{{defaultList}}" wx:key="index">
<list defaultList="{{item}}"></list>
</block>
//搜索失败
<view class="blank" wx:if="{{searchError}}">
<image class="blank-img" src="https://s1.hdslb.com/bfs/static/jinkela/space/assets/nodata02.png"/>
<text class="blank-text">找不到该用户~</text>
</view>
模糊查询
-
搜索成功
-
搜索失败
-
监听输入值,通过
onChange()
随时监听搜索框输入的值并将值赋给value
onChage(e){
// console.log(e);
this.setData({
value:e.detail
})
}
- 封装查询函数
queryArray()
通过输入的value
值实现查询,细节的是我为了实现模糊查询,使用indexOf
查询字符串,若字符串中至少有一个匹配成功则输出结果
async queryArray(){
let value=this.data.value
let arrList=this.data.arrList
for(let i=0;i<arrList.length;i++){
if(arrList[i].up.indexOf(value)>=0){
// console.log([arrList[i]]);
this.setData({
defaultList:[arrList[i]]
})
}
}
else{ //查询失败
this.setData({
searchError:true
})
}
},
- 合并数组,由于我是将特别关注的数据和默认关注的数据分别放在两个集合,但查询时我希望它们在一个数组里方便查询,所以需要将两数组合并,定义一个合并数组函数
addArray()
,需要的时候调用就ok
async addArray(){
let arr1= await db.collection('defaultList') //默认关注信息数组
.get()
.then(res=>{
return res.data
})
let arr2= await db.collection('specialConcern') //特别关注信息数组
.get()
.then(res=>{
return res.data
})
let arr=[...arr1,...arr2]
this.setData({
arrList:arr
})
},
- 调用函数
queryArray()
// 键盘查询
onSearch(){
this.queryArray()
},
// 点击查询
onClick(){
this.queryArray()
},
源码
结语
终于写完啦,(ಡωಡ)hiahiahia 在此非常感谢给予帮助的老师同学,在我遇到bug时恼到薅头发的时候及时救我于水火之中。如果你喜欢这篇文章或者可以帮到你,不妨点个赞吧!(๑• . •๑) 同时也非常希望看到这篇文章的你在下方给出建议!