记录云开发小程序的开发之路

624 阅读20分钟

本文记录如何从零开始如何一步步完成云开发小程序。该小程序分为tabBar页面、基础页面;页面中包含封装的自定义组件,使用第三方组件库vant协助开发;通过云函数更新修改数据库,开发具体流程见下:

项目前期准备工作

  • 编写全局wxss文件,设置小程序整体的样式。如height: 100%;box-sizing: border-box;

  • 自定义tabBar。

    • 修改app.json文件配置

    • 创建custom-tab-bar文件夹,按照自定义组件的形式编写四个文件。

    • 该组件展示时,使用vant组件库中的Tabbar标签栏。

    • 绑定change事件。当标签栏发生变化时触发该事件,实现页面跳转。

      默认情况下,event.detail拿到当前选中项的索引;标签指定name的情况下,event.detail拿到当前选中项的name。

  • vant组件库。

    • 下载包npm i @vant/weapp -S --production
    • 修改app.json文件配置
    • json文件中引入,wxml文件中使用。
  • 云开发。

    • 修改project.config.json文件配置;

      编写全局app.js文件,在onLaunch函数中,通过wx.cloud.init({})手动指定环境;

    • 下载包npm install wx-server-sdk

    • 如果需要导入数据库的话,点击云开发控制台,点击数据库,新建集合,点击导入文件。

      三个集合。userInfo、animal、goods。

    • 新建cloud文件夹,新建Node.js云函数文件夹,编写函数,上传到服务器。

封装自定义组件

  • 每个宠物的基本信息animal-card组件

    • 该组件展示宠物的基本信息,组件用到原生标签slot插槽标签;vant组件库中van-tag标记组件。

      该整体组件绑定tap点击事件,进行页面跳转,跳转到宠物详细信息页面。未传参。

      设置插槽,使自定义组件更加自由。

    • js文件中。

      • options配置对象中,开启多插槽。
      • 在properties中设置父组件传递过来的参数类型。
    • 页面跳转的函数。

      • 使用wx.navigateTo方法
      • 跳转到宠物详细信息页面,把当前宠物id传过去。url采取模板字符串的形式。
  • 每个商品信息goods-card组件

    • 该组件展示商品信息,组件用到原生标签radio标签;vant组件库中van-card商品卡片组件、van-stepper步进器组件。

      两栏flex布局,左边是单选按钮,右边是card。

      radio标签有是否选中、禁用等属性;绑定tap点击事件,更改当下商品是否选中状态的函数。

      van-card商品卡片组件有图片thumb、标签tag等属性;使用插槽slot="num",插槽中放入van-stepper步进器组件,可自定义van-card的结构

      van-stepper步进器组件有最小值、最大值、步长、禁用、value等属性;受控组件,实现双向数据流:绑定change事件,更改当下商品数量。value的值为data中的数据,当输入框中的value改变时触发函数,通过e.detail得到value的值,实时改变data中的数据,进而实时更新标签中value属性的值。

    • 在properties中设置父组件传递过来的参数类型。

    • 更改当下商品是否选中状态的函数。

      • 商品是否选中状态取反,更新当下商品对象

      • 通过this.triggerEvent()方法触发父组件传递的自定义事件,并向父组件传递参数,如该商品id、商品是否选中状态、商品数量。

        父组件中更新整个goodsList商品列表

    • 更改当下商品数量的函数。

      • 当输入框中的value改变时触发函数,通过e.detail得到value的值,实时改变data中的数据,进而实时更新标签中value属性的值,更新当下商品对象

      • 通过this.triggerEvent()方法触发父组件传递的自定义事件,并向父组件传递参数,如该商品id、商品是否选中状态、商品数量。

        父组件中更新整个goodsList商品列表

tabBar页面

  • 首页home

    • 配置json文件。窗口的背景颜色、窗口的标题、窗口的标题字体颜色;引入vant组件库中van-button组件;宠物基本信息animal-card组件。

    • 展示页面用到原生组件scroll-view、swiper;vant组件库中van-button;自定义组件ani-card组件。

      scroll-view组件设置scroll-view固定高度、绑定scrolltolower上拉触底加载事件、refresherrefresh下拉刷新事件。

      van-button组件绑定tap/click点击事件,选择宠物类型的函数。通过data-xx传参,把宠物类型作为参数传递给事件对象。

      自定义组件ani-card组件中,父组件向子组件传递数据列表渲染,多个父组件并列排布。

    • onLoad函数中。

      • 修改tabBar组件中data中的 active属性。
      • 动态获取窗口高度,减去Tabbar组件高度,动态计算得到scroll-view高度,更新到data中。
      • 调用获取宠物列表函数。
    • 获取宠物列表函数。

      • 调用getAnimalList云函数,传入参数为宠物类型、当前页码、每页展示数据的数量,并得到宠物列表;宠物列表更新到data中。
      • 若刷新被开启,需要手动关闭刷新。
    • cloud文件夹中编写getAnimalList云函数,返回宠物列表,数组的形式。

      • 在animal集合中,用到where()、limit()、skip()、get()方法,参数为宠物类型、当前页码、每页展示数据的数量,获取宠物列表。
    • 上拉触底加载事件,后台分页,减轻服务器和用户压力。调用获取宠物列表函数。

    • 下拉刷新事件。调用获取宠物列表函数。

    • 选择宠物类型事件。通过data-xx传参,通过event.currentTarget.dataset.xx获得参数。调用获取宠物列表函数。

  • 个人中心页mine

    • 展示页面用到vant组件库中van-notice-bar通知栏、van-cell单元格、van-cell-group。

      button标签,属性open-type="chooseAvatar",点击可实现选择头像功能,绑定chooseavatar用户选择头像事件。

      button标签中包含image标签,用户未登录时,显示灰色头像,头像地址在data中。

      van-cell单元格组件绑定tap/click点击事件,进行页面跳转。通过data-xx传参,把路径作为参数传递给事件对象。

    • onLoad函数中修改tabBar组件中data中的 active属性。

    • 用户选择头像事件。

      • event.detail拿到用户信息,event.detail.avatarUrl拿到头像。
      • 调用login云函数,传入参数为用户头像,得到该用户信息。返回结果存储在本地存储中;返回结果更新到data中。
    • cloud文件夹中编写login云函数,返回该用户信息。

      • 查询用户是否登录,用到where()、get()方法。
      • 用户未登录,需要添加注册用户信息,用到add()方法。
      • 用户已登录,需要获取用户信息,用到doc()、get()方法。
    • 获取用户信息的函数。

      • 每次显示个人中心页,都需要获取最新的用户信息,从数据库中取出最新的个人信息,更新到data中。
      • 数据库中取出最新信息,用到doc()、get()方法,参数为用户id。
    • 页面跳转的函数。封装函数,统一实现页面跳转。

      • 使用wx.navigateTo方法。
      • 通过data-xx传参,通过event.currentTarget.dataset.xx获得参数,分别跳转到我的云养、我的关注、个人资料页面。
    • 退出登录的函数。清空浏览器本地存储,删除data中数据,但不会影响数据库。

基础页面

  • 宠物详细信息页面animalInfo

    • 展示宠物的详细信息,组件用到原生组件轮播图swiper、vant组件中的van-button、van-icon、商品导航van-goods-action/van-goods-action-icon/van-goods-action-button组件。

      van-button组件的属性open-type="share",点击按钮可以实现转发功能

      van-goods-action-icon图标组件绑定click点击事件,返回首页。未传参。

      van-goods-action-icon图标组件绑定click点击事件,关注/取消关注。未传参。

      van-goods-action-button按钮组件绑定click点击事件,跳转到云养支付页面。未传参。

    • onLoad函数中。

      • 拿到自定义组件animal-card跳转到宠物详细信息页面时,路由传过来的宠物id,更新到data中。
      • 拿到本地存储中的用户id,更新到data中。
      • 调用获取宠物详情函数。
      • 设置当前页面的转发按钮。设置为发送给朋友、分享到朋友圈。
    • 用户点击关注 || 取消关注函数。

      • 用户已登录,提示用户操作中;调用patchLike云函数,更改该用户信息,无返回值;调用获取宠物详情函数;关闭提示。
      • 用户未登录,提示用户登录,返回函数。
    • cloud文件夹中编写patchLike云函数,更新该用户信息。

      • 在userInfo集合中获取该用户信息的JSON对象数据,用到doc()、get()方法,参数为用户id。

      • 在userInfo集合中更新该用户信息的JSON对象数据,用到doc()、update()方法,参数为用户id。

        用户信息JSON对象中添加likeAnimalIds属性,属性值为关注宠物的id的数组,更新方法无返回值。

        利用数组的includes()方法判断数组中是否包含该宠物id。如果likeAnimalIds数组中有该宠物id,则取关,利用数组的filter()方法,筛选出不是该宠物id的宠物们;如果ikeAnimalIds列表里没有该宠物id,则关注该宠物,利用ES6的语法,数组中增加该元素。

      • 该云函数返回静态数据,message、code等。

    • 获取宠物详情函数。

      • 用户已登录,调用getAnimalInfo云函数,传入参数如该用户id、该宠物id,得到该animalId宠物的详细信息 + like属性。
      • 用户未登录,用到doc()、get()方法,参数为该宠物id,获取该animalId宠物的详细信息。
      • 宠物的详细信息更新到data中。
    • cloud文件夹中编写getAnimalInfo云函数,返回该animalId宠物的详细信息 + like属性。

      • 在userInfo集合中获取该用户信息的JSON对象数据,用到doc()、get()方法,参数为用户id。

        在animal集合中获取该宠物信息的JSON对象数据,用到doc()、get()方法,参数为宠物id。

      • 返回该animalId宠物的详细信息,并添加like属性。like属性的值为布尔值,表示该用户的likeAnimalIds数组中是否含有该宠物id。

    • 返回首页函数。返回之前某个tabBar页面,即主页,用wx.switchTab()方法。

    • 进入云养支付页面函数。

      • 用户已登录,wx.navigateTo()方法。宠物详情页面跳转到云养支付ralsePay页面,把当前宠物id传过去。
      • 用户未登录,提示用户登录。
    • 当用户用户点击按钮分享、用户点击页面右上角分享时,自定义分享内容。

      • title;path是被分享者进入小程序时的页面。

        分享时传递该宠物id,进入页面后onLoad执行,获取宠物详情函数正常调用,才能正常显示宠物信息。

  • 云养支付页面ralsePay

    • 展示页面用到原生标签radio单选框;vant组件库中的van-image、van-tag标记、van-submit-bar提交订单栏;自定义组件ani-goods-card。

      ani-goods-card自定义组件中。

      • 父组件向子组件传递数据列表渲染,多个父组件并列排布。
      • 绑定setCheckedNum自定义事件,父向子传递函数,子中触发函数并传递参数给父。

      van-submit-bar提交订单栏组件有价格、按钮文本等属性;绑定submit提交事件,提交订单,进行支付的函数;包含radio标签。

      radio标签有是否选中等属性;绑定tap点击事件,点击全选按钮的函数。

    • onLoad函数中。

      • 拿到宠物详细信息页面跳转到云养支付页面时,路由传过来的宠物id,更新到data中。
      • 拿到本地存储中的用户id,更新到data中。
      • 调用获取该宠物信息函数。
      • 调用获取商品列表函数。
    • 获取该宠物信息函数。

      • 用到doc()、get()方法,参数为该宠物id,获取该animalId宠物的详细信息;更新到data中。
    • 获取商品列表函数。

      • 调用getGoodsList云函数,未传参,查询全部商品,得到所有商品的数组/列表。

      • 对商品列表进行二次处理。利用数组forEach()方法遍历商品列表,为数组中每一项元素item设置checked属性为false、value属性为1。

        初始情况下,每个商品为未选中状态,选择数量为1。

      • 商品列表更新到data中。

    • cloud文件夹中编写getGoodsList云函数。

      在goods集合中,用到where()、get()方法,参数为{},查询全部商品,返回所有商品的数组/列表。

    • 点击全选按钮的函数。

      • 取出全选按钮的状态,对状态取反。
    • 利用数组forEach()方法遍历商品列表,如果商品的库存数量amount大于0,该商品的选中状态和全选按钮状态设为一致。

      • 全选按钮的状态、商品列表更新到data中。
      • 调用计算价格函数。
    • 自定义事件setCheckedNum。作用是某个商品的选中状态、数量变化时,更新整个goodsList商品列表,并重新计算价格。

      • 通过event.detail获取子组件ani-goods-card自定义组件传递的参数,包括商品id、商品是否选中状态、商品数量value。
    • 利用数组forEach()方法遍历商品列表,更新整个goodsList商品列表。

      • 调用计算价格函数,重新计算价格。
    • 计算价格函数。

      • 利用数组forEach()方法遍历商品列表,如果选中了该商品,总价上增加该商品的价格乘以数量。
    • 提交订单,进行支付的函数。

      • 利用数组filter()方法遍历商品列表,筛选出选中的商品,返回新数组。
    • 利用数组map()方法遍历新数组,每个商品对象变成一个新对象,对象中包含_id商品id、value商品数量,返回新数组payData。

      • 如果新数组payData长度大于0,调用postPay云函数,传入参数为该用户id、该宠物id、payData、前端计算总价,获取返回结果包括code和message。
      • 根据返回结果中的message提示用户;根据返回结果中的code若为真,通过wx.redirectTo()方法跳转到我的云养myCloudRalse页面。
    • cloud文件夹中编写postPay云函数,返回静态结果如code、message。

      • 在userInfo集合中获取该用户信息的JSON对象数据,用到doc()、get()方法,参数为用户id。

      • 利用数组map()方法遍历payData数组,将每个对象变成每个商品的id,返回新数组ids。

      • 在goods商品集合中找出符合新数组ids的所有数据,即用户选择想要购买商品们的数组,用到where()、db.command.in()、get()方法,参数为新数组ids。

        返回结果解构出data,是数组的形式,每个数组元素是商品对象。

      • 利用数组filter()方法遍历data数组,方法中利用数组find()方法遍历payData数组,找到同一商品,筛选出前台选择商品数量大于后台商品库存数量的商品,若筛选数组的长度大于0,返回静态结果,code为0,message为商品不足。

      • 利用数组reduce()方法遍历data数组,方法中利用数组find()方法遍历payData数组,找到同一商品,通过后台商品的价格乘以前台选择商品数量,计算商品的后台总价,累加返回结果。

        若商品的后台总价和前端计算总价不相等,返回静态结果,code为0,message为商品信息发生变化请重新支付。

        若商品的后台总价大于用户余额,返回静态结果,code为0,message为支付失败,余额不足。

      • 更新该用户信息,余额自减,爱心值自增。用到doc()、update()、db.command.inc()方法,参数为后台总价。

      • 更新商品数据库,每个商品库存数量自减。

        利用数组map()方法遍历payData数组,对每个数组元素用到doc()、update()、db.command.inc()方法,参数为前台选择商品数量,每个数组元素返回一个promise对象,map()方法返回promise数组。Promise.allSettled() 方法处理promise数组,更新商品数据库。

      • 该云函数中调用patchRalse云函数,更新该用户信息,传入参数为该用户id、该宠物id,无返回值。

      • 返回静态结果,code为200,message为支付成功。

    • cloud文件夹中编写patchRalse云函数,更新该用户信息。

      • 在userInfo集合中获取该用户信息的JSON对象数据,用到doc()、get()方法,参数为用户id。

      • 在userInfo集合中更新该用户信息的JSON对象数据,用到doc()、update()方法,参数为用户id。

        用户信息JSON对象中添加cloudRalse属性,属性值为云养宠物的id的数组,更新方法无返回值。

        在数组后添加新云养的宠物id,需要去重,set对数组去重后为set对象,Array.from把set对象转换为数组。

      • 返回更新后的该用户信息。

  • 我的云养页面myCloudRalse

    • 展示页面用到vant组件库中的van-search搜索框、van-icon、van-tag、van-empty空状态;自定义组件ani-card组件。

      van-search搜索框通过model:value实现简易双向数据流,可同时实时更新data、输入框中的value值;绑定search事件,用户点击键盘上的搜索或回车时触发;使用插槽,表示搜索框右侧按钮,插槽标签中绑定tap点击事件,用户点击搜索按钮时触发。

      如果用户未登录,展示van-empty空状态组件;如果用户已登录,未云养宠物,展示van-empty空状态组件;

      如果用户已登录,已云养宠物,展示ani-card自定义组件,包含van-icon、van-tag组件。

      • 父组件向子组件传递数据列表渲染,多个父组件并列排布。
      • 其中van-icon、van-tag组件中使用自定义组件中的插槽
    • onShow函数中。因为每次进入我的云养页面,都希望展示最新数据,所以在onShow函数中调用函数。

      • 拿到本地存储中的用户id,更新到data中。也顺便放在onShow函数中。
      • 调用我的云养函数。
    • 我的云养函数。

      • 用户点击键盘上的搜索或回车时、用户点击搜索按钮时、onShow函数中调用时触发。
      • 调用getRalseList云函数,传入参数为用户id、搜索框中的内容,得到云养宠物列表,数组的形式,更新到data中。
    • cloud文件夹中编写getRalseList云函数,返回云养宠物列表,数组的形式。

      • 在userInfo集合中获取该用户信息的JSON对象数据,用到doc()、get()方法,参数为用户id。
      • 在animal集合中,用到where()、db.command.in()、get()方法,参数为用户信息中的云养数组cloudRalse、正则匹配对象,返回云养宠物列表,数组的形式。
  • 我的关注页面myLike

    • 展示页面用到vant组件库中的van-button、van-icon、van-tag、van-empty空状态;自定义组件ani-card组件。

      van-button组件中,icon图标是动态变化的,若降序排序则为向下箭头,若升序排序则为向上箭头;绑定tap点击事件,切换排序规则的函数。

      如果用户未登录,展示van-empty空状态组件;如果用户已登录,未关注宠物,展示van-empty空状态组件;

      如果用户已登录,已关注宠物,展示ani-card自定义组件,包含van-icon、van-tag组件。

      • 父组件向子组件传递数据列表渲染,多个父组件并列排布。
      • 其中van-icon、van-tag组件中使用自定义组件中的插槽
    • onShow函数中。因为每次进入我的专注页面,都希望展示最新数据,所以在onShow函数中调用函数。

      • 拿到本地存储中的用户id,更新到data中。也顺便放在onShow函数中。
      • 调用我的关注函数。
    • 切换排序规则的函数。

      • 对data中的排序规则取反,更新到data中。
      • 调用我的关注函数。
    • 我的关注函数。

      调用getLikeList云函数,传入参数为用户id、排序规则,得到关注宠物列表,数组的形式,更新到data中。

    • cloud文件夹中编写getLikeList云函数,返回关注宠物列表,数组的形式。

      • 在userInfo集合中获取该用户信息的JSON对象数据,用到doc()、get()方法,参数为用户id。
      • 在animal集合中,用到where()、db.command.in()、orderBy()、get()方法,参数为用户信息中的关注数组likeAnimalIds、排序规则,返回关注宠物列表,数组的形式。
  • 个人资料页面myInfo

    • 展示页面用到原生组件image、vant组件库中的van-field输入框、van-cell-group包裹输入框、van-button。

      image标签绑定tap点击事件,选择图片作为头像。

      van-field输入框通过model:value通过model:value实现简易双向数据流,可同时实时更新data、输入框中的value值。

      van-button组件绑定tap点击事件,提交信息,作用是更改服务端的用户昵称和用户头像。

    • onLoad函数中。

      • 拿到本地存储中的用户id,更新到data中。
      • 调用获取用户信息函数。
    • 获取用户信息函数。

      在userInfo集合中获取该用户信息的JSON对象数据,用到doc()、get()方法,参数为用户id。将返回数据的头像地址、昵称更新到data中。

    • 选择图片作为头像的函数。

      通过wx.chooseMedia()方法从手机相册中或拍摄中,选择图片,返回结果为buffer流形式的图片临时地址,更新到data中。

    • 提交信息的函数。

      • 比较服务端头像和当前页面头像,判断是否修改了头像。

        若修改通过wx.getFileSystemManager().readFileSync(xxx)将buffer流形式的图片临时地址转换为arrayBuffer流

      • 提示用户正在操作中。

      • 调用patchUserInfo云函数,传递参数为用户id、用户昵称、用户头像,更新该用户信息,无返回值。

      • 关闭提示,通过wx.navigateBack()方法返回上一页。

    • cloud文件夹中编写patchUserInfo云函数,更新该用户信息。

      • 得到前端传递的数据,用户id、用户昵称、用户头像。

        在userInfo集合中获取该用户信息的JSON对象数据,用到doc()、get()方法,参数为用户id。

        通过用户头像数据是否为空,判断用户是否修改了头像。若修改通过cloud.uploadFile()方法上传新头像,参数是对象的形式,包含cloudPath、fileContent属性;通过cloud.deleteFile()方法删除旧头像,参数是对象的形式,包含fileList属性;新头像赋值到该用户信息的头像属性。

      • 在userInfo集合中,通过doc()、update()方法,更新该用户信息中的用户昵称、用户头像。

      • 返回该用户信息。

采坑修复

  • 改变数据库中某个集合的权限,创建者可读写改为所有用户可读,创建者可读写。这样未登录的用户也可以看到信息。
  • 服务类目设置为工具-信息查询,小程序上线。
  • 首页不显示,审核未通过,在设置中勾选“不校验合法域名、web-view(业务域名)、TSL版本以及HTTPS证书”。