相册小程序实现,结合云开发,实现多张上传和分页加载照片,删除照片无需重新加载

最近折腾了一下微信小程序,发现小程序开发起来有它自己独特的魅力。很多常用的功能,如加载提示(wx.showLoading),都可以用一句代码实现。配合上云开发,整个开发速度得到了有力的提升。本文章,介绍如何开发一个相册小程序。

以下是小程序的GitHub链接:

https://github.com/CQJames/PhotoAlbumProgram

先看一下效果图:

  1. 上传照片效果展示,最多支持9张照片上传。
    相册小程序实现,结合云开发,实现多张上传和分页加载照片,删除照片无需重新加载_第1张图片

  2. 查看我的照片墙效果展示,下拉刷新重载,上拉分页加载,一次5条:
    相册小程序实现,结合云开发,实现多张上传和分页加载照片,删除照片无需重新加载_第2张图片
    相册小程序实现,结合云开发,实现多张上传和分页加载照片,删除照片无需重新加载_第3张图片

  3. 删除某张照片,无需重新加载照片列表,即删除一张,再加载一张新的(如果有的话)。
    相册小程序实现,结合云开发,实现多张上传和分页加载照片,删除照片无需重新加载_第4张图片

  • 最下面的标签实现
"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,初始化云,并获取用户基本信息与openId(为了之后标识是该用户的照片)。
//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:


  
    
    
      上传者:{{item.uploader}}
      上传时间:{{item.time}}
      
        
  

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次,拒绝再重新请求。并且给用户反馈友好的失败信息。所以说,学无止境,是不是。

你可能感兴趣的:(微信小程序)