某相册类小程序项目总结

1.项目简介

一款为家庭设计的亲密社交产品,分为云端存储、智能电视、小程序三个平台,小程序端主要功能包括:

  • 建立相册,上传图片、视频,
  • 单张图片(视频)的预览、分享、下载、评论
  • 相册集预览、分享、下载、评论
  • 相册内容管理,删除、下载、设置封面、重命名、设置成开机视屏及屏保图片等
  • 照片共享,手机、电视多端家庭内容同步和管理
  • 邀请、删除家庭成员,添加、删除绑定设备
  • 个性化设置家庭昵称、自己昵称、相册名称

2. 主要工作和疑难点汇总

2.1 主要工作
  1. 封装api请求,改造wx.requeset方法,封装http请求
  2. 抽取公共样式文件,在每个page文件夹的.wxss文件中,通过import 引入,
    如:@import "../../lib/base.wxss";
  3. 抽取公共组件,如单个相册组件、弹出卡片组件、个人头像组件、照片卡片组件,通过在各个页面配置usingComponents参数使用。
  4. 封装全局公共函数
  5. 业务逻辑
  6. 接入百度统计, 统计实时数据

      在我的应用中添加小程序appkey,下载解压后的js文件到utils文件夹中去,同时将百度添加到request合法域名中去。
    
2.2 业务亮点
2.3 疑难点汇总
  1. 如何在小程序中使用less,可以实时转化为 .wxss文件?
  2. 小程序Page里的函数比app.js先执行的解决办法
  3. fixed 元素 auto 不生效原因
  4. 封装一个有输入框的modal层组件
  5. 微信小程序去除button默认边框样式
  6. 小程序如何获取点击元素信息
  7. 小程序如何在页面间传递数组对象?
  8. 小程序如何批量上传图片

    
    chooseImage、chooseVideo的回调函数中,wx.uploadFile
    上传,更新进度
    this.data.updated_length + 1
    
    当所有照片都上传成功,updated_length == total_length时,
    显示完全上传完毕
    
    视频的进度显示和图片的不一样
    图片是每次上传成功一张,updated_length + 1
    视频是调用
    wx.uploadFile 对象的 onProgressUpdate 函数,看到视频上传进度,每500毫秒更新一次
    
    
    
  9. 小程序几个组件
  10. 如何让swiper 跳转到点击的index ?

    current 参数
    preview了, 还能点击图片么,  失败,不使用
    https://www.cnblogs.com/BlueCc/p/10172742.html
  11. 动态设置小程序背景图片
  12. 如何实现分享、点赞功能

    分享: onShareAppMessage, 点赞:根据本人是否点赞过,是否有点赞权限
    
    onShareAppMessage: function(res) {
        var obj = {
          from: 'sharephoto',
          mac: app.globalData.mac,
          open_id: app.globalData.open_id,
          member_id: app.globalData.current_member.member_id,
          family_id: app.globalData.family_id,
          album_id: this.data.album_id
        }
        obj = JSON.stringify(obj);
        var name = app.globalData.current_member.nick_name;
        var shareObj = {
        title: `${name}跟你分享了一本有趣的相册集‘${this.data.album_title}’`,        // 默认是小程序的名称(可以写slogan等)
           path:`pages/login/login?message=${obj}`,
        imageUrl: this.data.album_cover,     //自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径,支持PNG及JPG,不传入 imageUrl 则使用默认截图。显示图片长宽比是 5:4
        success: function(res){
        },
        fail: function(){
        }
      };
      return shareObj;
      },
            
    
    
    
    //节流,300ms才能点一次
    if (this.timer) {
      clearTimeout(this.timer)
    }
    this.timer = setTimeout(() => {
      console.log('点击点赞', this.data.like_id);
      server.like(family_id, this.data.album_id, mac, member_id, open_id, app.globalData.open_id, form_id,  this.data.like_id).then((res)=>{
        this.getPhotoBlock();
      })
    }, 300)
    
  13. 如何实现全选、单选功能

    对某一天日期的照片,如果没张都选了,传递今天整个相册
    每次单选照片都会重新判断是否全选
    
  14. 小程序时间过滤器 formatTime util的使用

    wxs中new Date()等js方法不可用,  所以不能用过滤器,还是用方法
    
  15. 为什么多个 formId 会重复,

    
    因为不支持同时获取多个 formId, 每次只能获取一个
  16. 如何实现预览照片,点击后跳转到单张照片?

    wx.previewImage,这里注意 swiper中,currentIndex 左右滑动是否一致,change函数的处理。
    
  17. 如何支持同时预览图片和视频?

    
    直接使用 wx.previewImage缺点:不能支持视频,不支持对单张照片做其他操作,智能预览,所以先跳转到 swiper页面
    点击照片,预览单张照片。点击视频,跳转到vediofull页面。
    
    
  18. 视频播放是否全屏

    videofullchange,监听全屏事件,小程序视频根据尺寸判断全屏
    在 chooseVideo 的时候, 获取视频的高宽
    
  19. 解决,如果有其他照片上传失败怎么办?

    每次调用 showProgress
    
  20. 如何判断小程序来源?分享?邀请?

    this.data.message.from几个值判断,一共有9种情况
    
    邀请、分享、在家庭中,不在家庭中,是不是管理员,是否来源扫一扫
    
    单张相片
    相册
    受到邀请,不在家庭
    受到邀请,在家庭中
    扫一扫,不在家庭中
    扫一扫,在家庭中,是管理员
    扫一扫,在家庭中,不是管理员
    不是成员,不是扫一扫
    已经在家庭中
    其他
    
    
    
  21. showActionSheet 有长度限制吗?

    
    有6个,超过怎么办?二层底部弹卡
  22. 如何见人照片与视频
  23. 内容过滤
  24. 接入百度移动统计
  25. 授权问题

    
    问题出现在,分享给第三人单张照片的时候,未先授权小程序前,不能查看照片,改变login页面逻辑,去掉入门授权,在点击分享、下载时候再询问授权
    首页、家庭页,操作后才授权,点击前会有蒙层
  26. 安全问题

    需要操作的页面,onLoad都会 checkInFamily,如果不在任何家庭中,跳转到scan页面
    
  27. 哪些情况下展示红包?

    
    创建相册、邀请成功成员
  28. 如何通过扫描二维码获得数据

    wx.scanCode, 获取返回参数
  29. 如何判断自己有没有全选评论、点赞、编辑?

    如果照片、视频来源于分享者,且分享人的id=评论id,用分享人的信息给后端传递参数。无论是获取评论、删除评论、发送评论,被分享人都是使用的分享者信息。

  30. 一级tab页面需要哪些验证?

    1,首先检查有没有授权,wx.getUserInfo,授权后下一步操作  
    2,检查checkIn,在不在家庭中,有没有操作权限,没有退出  
    3,获取成员信息,检查有没有红包,有,领取后下一步操作
    
  31. 如何拿到信息扫描?

    
    wx.scanCode({
          success: (res)...
          
      通过res值获取
  32. 获取验证码逻辑

    me.data.timerFun = setInterval(function () {
        if (me.data.timer > 0) {
        me.setData({timer:me.data.timer-1})
        }else {
        me.setData({timer:'重新发送'})
          clearInterval(me.data.timerFun);
        }
        }, 1000);

4. 业务逻辑梳理

4.1 项目哪几个page组成?有几个组件?

| 16个page  | 5个组件 |
|  ----  | ----  |
| login  | 获取token、管理跳转 |
| le_login  | 同步账号 |
| about | 账号绑定、消息、关于、意见反馈 |
|  photo-edit | 照片编辑页 |
| select-device | 选择屏保页面 |
| h5 | 红包页面 |
| comment | 评论列表页面 |
| swiperphotos | 视频、照片滑动页面 |
| vediofull | 全屏播放视频页面 |
| photomanage | 照片管理页面,全选、反选、下载 |
| photos | 相册所有照片页面 |
| one-photo | 单张照片分享页面 |
| homepage | 首页,我的相册页面 |
| familypage | 家庭首页 |
| del-member | 删除成员、设备页面 |
| my-modal | 弹卡组件|
| member-icon | 头像组件|
| photo-album | 相册组件|
| photo-detail | 相册详情组件|
| red-packet | 红包组件|

4.2 挑几个页面看看
  1. family 页面?

    1.1 邀请成员

    主要通过 onShareAppMessage 函数,将邀请人的信息添加在 path的参数中,在login页面中获得
    

    1.2 添加设备

    调整到 homescan页面
    

    1.3 退出家庭

    
    //如果只剩本人自己, 解散家庭,否则按照退出家庭算
    
    
    
  2. photomanage 页面?(点击,下载、设置壁纸,设置屏保后跳转的页面)

    全选,反选逻辑
    
    
    
    设置封面逻辑,如何做到 所有天,只有一个封面?每一天的照片、视频,是一个组件
    
    select_photos  所有选中的照片
    
    
    选照片、下载、删除逻辑:
    传值过来的是 按日期分布的数组,按照日期对应,修改当天的照片数组。
    遍历所有天的照片,计算选中张数,编号。
    
    设置封面逻辑:
    所有天照片,只有有一天选中了,其他所有置灰。
    
    
    
  3. photos 页面? 点击相册进入的页面

    功能:上传照片、视频,点赞、评论    
    
    
  4. vediofull 页面

    首先需要创建视频播放上下文对象   
    wx.createVideoContext('myVideo');
    
    退出:
    视频对象 pause,退出全屏, 对象置为null
    
    监听是否需要横屏:
    如果视频宽度大于高度,横屏
    
    
  5. swiperphotos 页面

    如何支持,同时预览照片和视频?  
    不使用原生自带的 wx.previewImage
    视频,跳转到 vediofull 页面
    
    定位到当前照片是,所有 swiper数组的第几章照片  
    初始状态,当前滑动照片数,预览照片上面的显示数字,current_index,和 swiper组件绑定的,current值差1,change函数滑动照片,改变current_index值
    
    
    

5. 几个组件简介

  1. member-icon:

        
        支持头像组件两种形态:文字在头像下方、文字在头像右方
  2. my-modal:

        支持弹出会话层有input文本框,支持编辑和新建功能
        
        新建相册名称为空,编辑相册名称为相册名称,怎么做到的?
        新建,文本框内容为update_value,编辑为从父类传过来的数据,textvalue
        
        如何在操作完编辑后,新建,相册名称为空?
        每次确认后,input框内容置空
        
        实时计算文本框字数?
        bindinput函数
        
        
                 
  3. photo-album: 相册组件

        
        每个相册组件,点击跳转到该相册详情页面
        
  4. red-packet: 红包组件

        
        //将红包信息参数发送给后端,传递给前端一个web-view 地址链接
        //webview src指向网页的链接。(承载网页的容器,会自动铺满整个小程序页面)
        <web-view src="{{link}}"></web-view>
    
  5. photo-detail: 相册详情组件,支持同一天照片全选、反选,设置屏保、删除、下载等功能

        如何区分对照片的操作类型?设置封面?下载?删除?根据前一个页面传过来的操作类型判断
        photo-detail只是一天的照片、视频操作,如何将所有日期选中照片传递给后端?
        每次触发某一天的照片,是一个数组,向父元素触发事件,
        
        this.data.photo_block.forEach((item, index)=> {
         if(item.days == photo.days) {
           this.data.photo_block[index] = photo;
         }
       })
       
         
         如何统计总数?
         每次重新计算选中照片。遍历。
    

6. 问题汇总解答

1. 如何在小程序中使用less,可以实时转化为 .wxss文件?

微信小程序只支持原生css写法,但是很浪费时间,使用 wxss-cli 可以实时将编写的 .less 文件自动编译为 .wxss 文件

1、npm或者yarn全局安装wxss-cli

npm install -g wxss-cli

2、运行wxss-cli命令(miniProject为小程序目录),less文件保存时自动编译

wxss ./miniProject

参考资料

2. 小程序Page里的函数比app.js先执行的解决办法

问题描述:
当我们初始化一个小程序时,默认文件 app.js 中有onLaunch函数,

onLaunch: function () {
    console.log("onLaunch");
    wx.login({
      success: res => {
        console.log("login");
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
      }
    })
}

默认目录,"pages/index/index", 中index.js 有 onLoad函数

onLoad: function () {
    console.log("index onLoad");
}

小程序网络请求默认为异步请求,在app.js的onLaunch运行后进行异步请求时,程序不会停止,index.js页已执行onload, onload里面的数据会因为没有获取到app.js里的东西而报错, 我们希望onLaunch执行完后再执行onLoad。

他们的执行顺序是,onLaunch > index onLoad > login
我们希望的执行顺序是:
onLaunch > login > index onLoad

解决办法

  1. 定义回调函数, onload里获取不到东西就一直获取,不执行下一步操作,直到获取到app.js的数据才继续执行。若login返回为空,则给app.js注册一个loginSuccessCallback回调,这个回调方法的执行时机,就是app.js中的异步请求完毕
  2. 把 app.js 中的 onLaunch 中方法拿到 index.js 文件中,按照自己的逻辑写
  3. 使用promise

方法1:

App({
  onLaunch: function () {
    wx.login({
      success: res => {
        this.globalData.checkLogin = true;
        //由于这里是网络请求,可能会在 Page.onLoad 之后才返回
        // 所以此处加入 callback 以防止这种情况
        if (this.checkLoginReadyCallback){
          this.checkLoginReadyCallback(res);
        }
      }
    })
  },
  globalData: {
    checkLogin: false
  }
  
  ...
})
 

//index.js
//获取应用实例
const app = getApp()
 
Page({
  data: {
    test: false
  },
  onLoad: function () {
    let that = this;
    //判断onLaunch是否执行完毕
    if (app.globalData.checkLogin){
      that.setData({
        test:true
      })
    }else{
      app.checkLoginReadyCallback = res => {
            //登陆成功后自己希望执行的,和上面一样
        that.setData({
          test:true
        })
      };
    }
  }
})

方法2:
把 app.js 中的 onLaunch 中登陆后才执行的方法拿到 index.js 文件中,这是最简单的方法

//index.js

onLoad: function () { 
    wx.login({
      success: res => {
        resolve(res); 
      }
    })
}

方法3:

// app.js中定义一个新的方法
App({
  onLaunch: function () {
      ...
  },
  getAuthKey: function (argument) {
    var that = this;
    return new Promise(function(resolve, reject){
        wx.login({
          success: res => {
            resolve(res); 
          }
        })
    })
  }
  ...
  
})

//index.js
onLoad: function () {
    ...
        
    app.getAuthKey().then(function(res){
      console.log('res')
    })
 }

参考资料: 参考1 参考2 参考3

3. fixed 元素 auto 必须要同时设置 top、left
position: fixed;
top: 132rpx;
left: 30rpx;
width: 690rpx;
margin: 0 auto;
4.封装一个有输入框的modal层组件

其实很简单,就是在modal中添加新的 input,

<view>
    <modal class="modal" wx:if="{{!hiddenModal}}"
     title="{{title}}" confirm-text="确定" cancel-text="取消" bindconfirm="modalconfirm" bindcancel="modalcancel">
        <view class="input-line">
            <input placeholder='请输入内容' maxlength="{{ maxlength }}" bindinput='input' type="text" type="text"  value="{{ textvalue }}" />
            <text>{{ currentlength}}/{{ maxlength }}</text>
        </view>
    </modal>
</view>

.modal{
    width: 540rpx;
    max-width: 540rpx;
    border-radius: 28rpx;
    .input-line {
        display: flex;
        border: 2rpx solid rgba(0, 0, 0, 0.05);
        font-size: 28rpx;
        padding: 16rpx;
        height: 40rpx;
        line-height: 40rpx;
    }
    input,  text{
        display: inline-block;
        vertical-align: top;
    }
    input {
        flex: 1;
    }
    text {
        width: 90rpx;
        color: #FFA004 ;
    }
}

5.微信小程序去除button默认边框样式
button::after{
    border: none;
}
6.小程序如何获取点击元素信息

使用驼峰模式,给点击元素绑定 data-*,通过 event.currentTarget.dataset 获取

<image src="{{ item.mini_pic }}" class="{{ item.show_opacity ? 'show_opacity' : ''}}" bindtap="tap" data-message="{{ item }}">
</image>

// 获取的点击节点元素是一个对象
 tap: function(event) {
    var message = event.currentTarget.dataset.message;
}
7. 小程序如何在页面间传递数组对象?

方法1:A页面跳转链接添加参数,B页面onLoad 接受
方法2:设置全局变量 globalData,用的少,一般适用于全局共享的一份信息,如用户open_id等

// A页面
// 数组、对象都需要stringify
var listData = JSON.stringify(that.data.listData)
var taskArray = JSON.stringify(that.data.taskArray)
wx.navigateTo({
    url: '../workRecord/updateBatch?listData=' + listData + '&taskArray=' + taskArray 
})

//B页面
onLoad: function (options) {
    var that = this
    var listData = JSON.parse(options.listData)
    var taskArray = JSON.parse(options.taskArray)
}



//A页面:
app.globalData.open_id = 3;
//B页面:
var lala = app.globalData.open_id;
8. 小程序如何批量上传图片
    chooseImage、的回调函数中,wx.uploadFile
    上传,更新进度



6. 其他

  1. 封装http请求

    class HTTP{

            request({url,data={},method='POST', header={'content-type':'application/json'} }){
                return new Promise((resolve, reject)=>{
                    this._request(url,resolve,reject,data, method, header)
                })
            }
            _request(url,resolve, reject, data={}, method='POST', header){
                wx.request({
                    url:url,
                    method:method,
                    data:data,
                    header: header,
                    success:(res)=>{
                        const code = res.statusCode.toString()
                        if (code.startsWith('2') && res.data.errno == 10000){
                            resolve(res.data)
                        }
                        else{
                            if(res && res.data && res.data.errmsg) {
                                this._show_error(res.data.errmsg)
                            }
                            else {
                                this._show_error(tips[1])
                            }
                            console.log('错111111111')
                            reject(res)
                        }
                    },
                    fail:(err)=>{
                        reject(err)
                        console.log('错22222222')
                        this._show_error(tips[1])
                    }
                })
        
            }
        
            _show_error(tip){
                if(!tip){
                    tip = tips[1]
                }
                wx.showToast({
                    title: tip,
                    icon:'none',
                    duration:2000
                })
            }
        }
    

迟不子
105 声望3 粉丝