赞
踩
最近折腾了一下微信小程序,发现小程序开发起来有它自己独特的魅力。很多常用的功能,如加载提示(wx.showLoading),都可以用一句代码实现。配合上云开发,整个开发速度得到了有力的提升。本文章,介绍如何开发一个相册小程序。
以下是小程序的GitHub链接:
先看一下效果图:
上传照片效果展示,最多支持9张照片上传。
查看我的照片墙效果展示,下拉刷新重载,上拉分页加载,一次5条:
删除某张照片,无需重新加载照片列表,即删除一张,再加载一张新的(如果有的话)。
"tabBar": { "list": [ { "pagePath": "pages/index/index", "text": "上传照片", "iconPath": "assets/pics1.png", "selectedIconPath": "assets/pics2.png" }, { "pagePath": "pages/album/album", "text": "我的照片墙", "iconPath": "assets/self1.png", "selectedIconPath": "assets/self2.png" } ], "selectedColor": "#3399FF" }
//app.js App({ onLaunch: function () { //初始化云 wx.cloud.init({ env: "***", traceUser: true }); //获取openId this.getOpenId(); // 展示本地存储能力 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) // 登录 wx.login({ success: function (res) { } }); // 获取用户信息 wx.getSetting({ success: res => { if (res.authSetting['scope.userInfo']) { // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 wx.getUserInfo({ success: res => { // 可以将 res 发送给后台解码出 unionId this.globalData.userInfo = res.userInfo // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 // 所以此处加入 callback 以防止这种情况 if (this.userInfoReadyCallback) { this.userInfoReadyCallback(res) } } }) } } }) }, getOpenId() { // 获取用户openid let that = this; wx.cloud.callFunction({ name: 'getOpenId', complete: res => { console.log('云函数获取到的openid: ', res.result.openId); that.globalData.openId = res.result.openId; } }) }, globalData: { userInfo: null, openId: "" } })
1. 在project.config.json中添加以下代码,指定云函数根目录
"cloudfunctionRoot": "cloudFunctions/"
2. 在云函数根目录下建立名为getOpenId云函数,添加以下代码
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {
//返回用户信息
return event.userInfo;
}
先在本地选择照片,再把照片存到云存储空间,再把图片相关信息存到云数据库。
1. 选择照片
chooseImages: function() { //回调函数里又有新的this对象,所以必须在外部保存this(即Page对象)引用 let that = this; wx.chooseImage({ count: 9, success: function(chooseRes) { //openId加生成唯一时间戳加图片索引作为图片的云路径前缀 let openId = app.globalData.openId; //获取要上传多少张图片 let length = chooseRes.tempFilePaths.length; wx.showLoading({ title: '上传中, 请稍后', }); //一张一张开始上传 that.uploadImages(openId, 0, chooseRes.tempFilePaths, length); }, fail: function(res) { console.log("选择相片失败"); } }) }
2. 递归实现一张照片上传完,再上传下一张
uploadImages: function (openId, index, images, length) { let that = this; //上传时间 let time = new Date(); let timeStamp = Date.parse(time).toString(); wx.cloud.uploadFile({ cloudPath: openId + timeStamp + index + '.png', filePath: images[index], success: function (res) { console.log("上传成功"); //从上传结果中获取云端图片的路径url let imageUrl = res.fileID; that.setData({ imageUrl: imageUrl }); //上传者昵称 let name = that.data.userInfo.nickName; //添加到云数据库 that.addImageList(name, time, imageUrl); }, fail: function (res) { console.log("上传失败"); util.showTip('第' + (index + 1) + '张照片上传失败!'); }, complete: function(res) { if ((index + 1) == length) { //如果是最后一张 wx.hideLoading(); }else { //否则,传下一张 index = index + 1; that.uploadImages(openId, index, images, length); } } }); }
3. 将图片相关信息添加至云数据库imageList集合
addImageList: function (name,time,url){ console.log(util.formatTime(time, "Y-M-D h:m:s")); //获得数据库引用 const db = wx.cloud.database(); //把上传的图片添加到数据库 db.collection("imageList").add({ data:{ uploader: name, time: util.formatTime(time, "Y-M-D h:m:s"), imageUrl: url }, success: function(res){ console.log("相片添加到数据库成功"); }, fail: function(res){ console.log("相片添加到数据库失败"); } }); }
每次点击我的照片墙,判断云端是否有新的照片数据,有则更新照片列表。否则,不更新照片列表。
用户可以下拉刷新照片列表,上拉分页加载照片列表。
1. 每次点击我的照片墙进入onShow生命周期函数处理
onShow: function () { let that = this; //根据openId找属于用户的图片列表 let openId = app.globalData.openId; const db = wx.cloud.database(); db.collection("imageList").where({ _openid: openId }).count({ success: function (res) { console.log("获取相片列表总数成功" + res.total); if (that.data.total != res.total) { //如果有数据刷新,重新加载第一页 wx.pageScrollTo({ scrollTop: 0 }); that.data.pageIndex = 1; that.fetchImageList("刷新中"); } } }); }
2. 下拉刷新照片列表
先在本页面json添加以下代码,以允许下拉刷新。
"enablePullDownRefresh": true
再在本页面js添加以下代码,以下下拉刷新照片列表。
onPullDownRefresh: function () {
//下拉刷新图片列表
this.data.pageIndex = 1;
this.fetchImageList("刷新中");
//停止下拉刷新
wx.stopPullDownRefresh();
}
3. 上拉分页加载照片列表
onReachBottom: function () {
if (this.data.pageIndex < this.data.pageCount) {
//如果没到最后一页,页码加1,并加载新的一页图片列表数据
this.data.pageIndex = this.data.pageIndex + 1;
this.fetchImageList("加载中");
} else {
util.showTip('没有更多照片了');
}
console.log(this.data.pageIndex)
}
fetchImageList: function (title) { let that = this; //根据openId找属于用户的图片列表 let openId = app.globalData.openId; let pageIndex = that.data.pageIndex; let pageSize = that.data.pageSize; const db = wx.cloud.database(); //先计算总数,才可以进行分页 db.collection("imageList").where({ _openid: openId}).count({ success: function (res) { console.log("获取相片列表总数成功" + res.total); let pageCount = Math.ceil(res.total / pageSize); let total = res.total; //根据不同需求的抓取显示不同的进程提示 wx.showLoading({ title: title, }); //分页获取图片列表内容 db.collection("imageList").where({ _openid: openId }) .skip((pageIndex - 1) * pageSize).limit(pageSize).orderBy('time', 'desc') .get({ success: function (res) { console.log("获取相片列表成功"); //选获取原先的图片列表 let tempImageList = that.data.dataList; if (that.data.pageIndex == 1) { //如果要显示第一页,无需拼接图片列表数据 tempImageList = res.data; }else { //否则,拼接新的图片列表数据 tempImageList = tempImageList.concat(res.data); } //更新数据 that.setData({ pageCount: pageCount, total: total, dataList: tempImageList }); }, fail: function (res) { console.log("获取相片列表失败"); }, complete: function (res) { wx.hideLoading(); } }); }, fail: function (res) { console.log("获取相片列表总数失败"); } }); }
点击删除,触发deleteImage事件,事件通过wxml里的data-获取该照片id和imageurl。
然后先根据id删除照片记录,再通过imageurl删除真正的照片存储。
最后要更新照片列表。为了提高用户体验,删除不刷新整个照片列表,而是采用以下策略。利用id找到要删除的照片在原来的照片列表中的位置,通过slice函数把该照片从该照片列表中去掉。然后判断云端是否还有一条或以上的照片列表数据还没加载。没有的话,直接重设该照片列表就好。如果有,需从云端加载一条新的照片数据回来,并拼接在照片列表尾部。最后,重设该照片列表。
以下是wxml:
<block wx:for="{{dataList}}" wx:key="item">
<view class='item-container'>
<image class='img' src='{{item.imageUrl}}'></image>
<view class='item-name-time'>
<text class='item-name'>上传者:{{item.uploader}}</text>
<text class='item-time'>上传时间:{{item.time}}</text>
<button class='item-delete' data-id='{{item._id}}'
data-imageurl='{{item.imageUrl}}' bindtap='deleteImage'>删除</button>
</view>
</view>
</block>
1. 删除某张照片,询问用户是否真的要删除
deleteImage: function (event) { let that = this; //询问用户是否删除 wx.showModal({ title: '提示', content: '确定要删除该照片吗?', success: function (res) { if (res.confirm) { //确定删除 wx.showLoading({ title: '删除中', }); //获得图片在数据库的id let id = event.target.dataset.id; const db = wx.cloud.database(); //从数据库删除该记录 db.collection('imageList').doc(id).remove({ success: function (res) { console.log("删除照片记录成功"); //获得图片在存储的fileId,先获取imageurl let imageUrl = event.target.dataset.imageurl; let fileId = util.getFileId(imageUrl); //从存储真正删除该图片 wx.cloud.deleteFile({ fileList: [fileId], success: function (res) { console.log("删除照片成功"); }, fail: function (res) { console.log("删除照片失败"); } }); wx.hideLoading(); //根据id更新图片列表 that.updateImages(id); }, fail: function (res) { console.log("删除照片记录失败"); wx.hideLoading(); } }); } } }); }
2. 根据id单独把要删除的照片从照片列表中删除,注意如何向云端请求补充当前页一条数据
updateImages: function(id) { let that = this; //删除后更新图片列表 let list = that.data.dataList; let dataList = null; //去掉删除的 for (let i = 0; i < list.length; i++) { if (list[i]._id == id) { let dataList1 = list.slice(0, i); let dataList2 = list.slice(i + 1, list.length); dataList = dataList1.concat(dataList2); break; } } //加载一张新的图片加入当前图片列表 let openId = app.globalData.openId; let pageIndex = that.data.pageIndex; let pageSize = that.data.pageSize; const db = wx.cloud.database(); //更新总数,页数,还有图片列表 db.collection("imageList").where({ _openid: openId }).count({ success: function (res) { console.log("获取相片列表总数成功" + res.total); let pageCount = Math.ceil(res.total / pageSize); let total = res.total; if ((dataList.length + 1) <= res.total) { //如果还有未加载数据,则从数据库取一条数据补充当前页 //根据不同需求的抓取显示不同的进程提示 wx.showLoading({ title: '刷新中', }); //分页获取图片列表内容,因为是当前页补充一条新数据, 所以跳过pageIndex * pageSize - 1条 db.collection("imageList").where({ _openid: openId }) .skip(pageIndex * pageSize - 1).limit(1).orderBy('time', 'desc').get({ success: function (res) { console.log("获取相片列表成功"); //把获得的新数据加到尾部 dataList = dataList.concat(res.data); //更新数据 that.setData({ pageCount: pageCount, total: total, dataList: dataList }); }, fail: function (res) { console.log("获取相片列表失败"); }, complete: function (res) { wx.hideLoading(); } }); }else { //没有还未加载数据 that.setData({ pageCount: pageCount, total: total, dataList: dataList }); } }, fail: function (res) { console.log("获取相片列表总数失败"); } }); }
------重要部分已经介绍完毕,其实上面还有许多有待改善的地方。细心的你可能已经发现,许多fail回调函数里并没有作出相应的错误处理。后来想了一下,对于一些云请求或更新,可以在fail函数里,再次发起请求或更新。当然,这可能又会引起一个新问题,就是一直失败怎么办。我觉得可以再判断失败请求的次数,如果失败次数超过3次,拒绝再重新请求。并且给用户反馈友好的失败信息。所以说,学无止境,是不是。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。