從入門到提升—失物招領小程序(雲開發+即時通信)

寫在前面

本文主要聊聊該項目的雲開發技術點和js邏輯,IM即時通信以後會寫。經過本文你將學習微信小程序雲函數的使用、組件間通訊的各類方法、根據功能需求設計js邏輯、promise相關用法、頁面分頁加載等。
github項目地址javascript

完整效果展現

項目分析

  • 本校內已有三個失物招領QQ羣,每一個羣1900+人(包括重複加羣),上課期間,天天每一個羣的信息數99+(包括重複發、圖片文字分開發等)。另外,據管理員稱,羣內雖然少有閒話,但失物的找回率不高,不少同窗不知道有信息公告或者由於信息太多而錯過了。
  • 微信上可搜索到的失物招領小程序有很多,考慮到用戶的心理,好心人和失主之間的聯繫功能,不少平臺都是直接展現聯繫方式,即便有經過學號登陸纔可查看信息,那也是對用戶信息的不負責。
  • 基於上述分析,這款小程序的需求:有分類,能搜索,快速發佈,即時聯繫,不用加一堆的羣,也不擔憂錯過了信息。明確了需求,咱們才能更好的設計代碼邏輯,下面開始!

發佈功能(本地代碼)

思路分析

  • 功能:發表前提醒受權;表單爲空不可提交;能多選圖片並上傳相關內容。
  • 困難點:使用promise.all實現先上傳圖片,拿到圖片的雲路徑後,再將用戶輸入的發佈信息所有上傳。
  • 實現:
    • 點擊"當即發佈"按鈕後,調用組件inputDetail中的方法checkUploadForm(帶參數releaseType,方便後面上傳數據是添加到失物招領的found集合仍是尋物啓事的lost集合),表單內容不爲空開始上傳;
    • 對上傳圖片的緩存路徑執行forEach,將每張圖片的緩存路徑傳遞給promise函數promiseUplodImg(函數resolve上傳完成後每張圖片的雲路徑)上傳圖片;
    • 同時將每張圖片執行的該函數push進一個數組中,再執行promise.all(該方法接收一個每一項都是promise函數的數組);
    • 最後,經過.then(接收一個數組,包含全部圖片的雲路徑)將用戶輸入的信息上傳。

代碼實現

// miniprogram/pages/release/release.js
data: {
    clickStatus: 0,
    releaseTypes: ['found', 'lost'],
  }
//當即發佈按鈕上綁定該方法
checkUploadForm: function () {
    let that = this
    this.setData({
      isUpload: true
    })    // 聲明上傳狀態
    this.inputDetail = this.selectComponent('#inputDetail')
    this.inputDetail.checkUploadForm(that.data.releaseTypes[that.data.clickStatus])
//調用組件inputDetail上的checkUploadForm方法,傳參found || lost
}
複製代碼
// // components/inputDetail/inputDetail.js
checkUploadForm(releaseType) {
  //releaseType 根據參數肯定發佈的類型是失物招領仍是尋物啓事
    let that = this
    let promises = []
    if (that.data.releaseText && that.data.thingsType) {
        that.setData({
            releaseType,
        })
        if (that.data.imgLocalPath.length > 0) {
          wx.showLoading({
            title: '正在發佈...'
          });

          that.data.imgLocalPath.forEach(element => {
            promises.push(that.promiseUplodImg(element))
          })
          // 遍歷緩存圖片路徑,將多個promise函數放入數組promises中
          // 拿到全部圖片的雲路徑
          Promise.all(promises)
            .then((imgCloudPath) => {
              console.log(imgCloudPath);
              that.uploadRelease(imgCloudPath)
            })
            .catch((err) => { console.log(err); })

        } else {
          //用戶沒有選擇圖片,則直接上傳發布信息
          that.uploadRelease()
        }
      } else {
        wx.showToast({
          title: '內容或分類爲空',
          image: '../../asserts/icons/my_icon_still@2x.png',
          duration: 1800
        })
      }
    },
    promiseUplodImg(element) {
      return new Promise((resolve, reject) => {
        //一次只能上傳一張圖片到數據庫
        const filePath = element;
        let a = filePath.lastIndexOf('.');
        let b = filePath.lastIndexOf('.', a - 1);
        let c = filePath.substring(b + 1, a);
        const cloudPath = c + filePath.match(/\.[^.]+?$/);
        wx.cloud.uploadFile({
          filePath,
          cloudPath,
          success: (res) => {
            console.log('上傳成功', res)
            resolve(res.fileID)
            //每張圖片的雲路徑
          },
          fail: (err) => {
            console.log('上傳失敗', err)
          }
        });
      })
    },
    uploadRelease(imgCloudPath) {
      let that = this
      wx.cloud.callFunction({
        name: "uploadInput",
        data: {
          releaseType: that.data.releaseType,
          releaseImg: imgCloudPath,
          releaseText: that.data.releaseText,
          thingsType: that.data.thingsType,
          releaseCall: that.data.releaseCall,
          releaseRemind: that.data.releaseRemind,
          //用戶輸入的所有信息
        },
        success: () => {
          wx.hideLoading();
          const releaseType = that.data.releaseType
          wx.switchTab({
            url: `/pages/${releaseType}/${releaseType}`
          })
          // 跳轉到發佈類型對應的頁面
          wx.hideNavigationBarLoading()
        },
        fail: (err) => {
          console.log(err);
        },
        complete: () => {
          that.setData({
            releaseText: '',
            imgLocalPath: [],
            thingsType: '',
            releaseCall: '',
            releaseRemind: false,
            releaseType: 'found',
            typeIndex: 0,
          })
        }
      })
    },
複製代碼

發佈功能(雲函數)

思路分析

  • 功能:調用雲函數,將發佈內容上傳。
  • 實現: 使用promise先將發佈的信息上傳至對應的集合,再將發佈記錄的id和發佈者的openId上傳至集合user-release。這樣存儲便於後面讀取數據渲染界面,每一條發佈記錄的id經過user-release集合與發佈者信息綁定在一塊兒。(id是雲開發數據庫中給每條記錄自動生成的_id)

數據庫記錄示例

代碼實現

// 雲函數uploadInput
const getTime = function () {
    return new Promise((resolve, reject) => {
      var now = new Date();
      var month = now.getMonth() + 1;    
      var day = now.getDate();           
      var hour = now.getHours() + 8;      //沒加8被坑
      var minute = now.getMinutes();          
      var time = ''
      if (month < 10) time += "0";
      time += month + "-";
      if (day < 10) time += "0";
      time += day + " ";
      if (hour < 10) time += "0";
      time += hour + ":";
      if (minute < 10) time += '0';
      time += minute;
      resolve(time)      
    })
  }
exports.main = async (event, context) => {
  const userInfo = event.userInfo
  const createTime = await getTime()
  return await db.collection(event.releaseType).add({
    data: {
      releaseType: event.releaseType,
      releaseImg: event.releaseImg,
      releaseText: event.releaseText,
      thingsType: event.thingsType,
      releaseCall: event.releaseCall,
      releaseRemind: event.releaseRemind,
      createBy: userInfo.openId,
      createTime: createTime,
      deleted: false
    }
  })
    .then(res => {
      return db.collection('user-release').add({
        data: {
          releaseId: res._id,
          userId: userInfo.openId,
          deleted: false,
        }
      })
    })
}
複製代碼

效果展現

加載發佈的信息(本地代碼)

思路分析

  • 功能:在「所有」頁加載所有信息,點擊類別加載不一樣類的信息。
  • 實現:
    • 在當前頁面調用雲函數getRelease,並將獲取到的頁面數據存入一箇中間變量middleArr(這樣便於作分頁/分類加載);
    • 而後調用函數showPage判斷當前點擊的類別,從middleArr中篩選出與點擊類別匹配的數據,並將數據放入releaseIndo傳給組件infoCard來展現用戶發佈的信息。

代碼實現

// components/infoCard/infoCard.js
// 獲取到的數據傳入組件進行渲染
  properties: {
    releaseInfo: {
      type: Array,
      value: []
  }
<!--components/infoCard/infoCard.wxml -->
<block wx:for="{{releaseInfo}}" wx:key="index" wx:for-item="releaseItem"> </block>
複製代碼
<!--miniprogram/pages/found/found.wxml 發佈信息展現頁-->
<view class="navbar">
  <scroll-view scroll-x="{{true}}">
    <category tabs="{{['所有','證件','書籍文具','電子設備','生活用品','其餘']}}" catch:onTitleChange="onTitleChange"></category>
  </scroll-view>
</view>
<view class="foundContent">
  <view class="inforCards">
    <infocard id="infoCard" class="infoCard" releaseInfo="{{releaseInfo}}"></infocard>
  </view>
</view>
複製代碼
// miniprogram/pages/found/found.js
data: {
    tabbarText: 'found',
    releaseInfo: [],  // 傳遞給組件的數據
    typeTitle: ['all', 'card', 'booktool', 'electroic', 'lifetool', 'others'],
    thingsType: 'all', //默認點擊的是‘所有’類
    middleArr: [],
  },
  onTitleChange(e) {
    //判斷用戶點擊的類別
    this.setData({
      thingsType: this.data.typeTitle[e.detail]
    })
    this.showPage()
  },
onShow: function () {
	//加載頁面,調用雲函數
    let that = this
    wx.showNavigationBarLoading();
    wx.cloud.callFunction({
      name: 'getRelease',
      data: {
        tabbarText: that.data.tabbarText,
        //根據點擊的tabbar=失物招領或尋物啓事,獲取不一樣集合中的數據
      },
      success: res => {
        that.setData({
          middleArr: res.result,
          //成功返回的數據放入中間變量
        })
        that.showPage()
      },
      fail: (err) => {
        console.log(err)
      },
      complete: () => {
        wx.hideNavigationBarLoading()
      }
    })
  },
   showPage() {
    let that = this
    if (that.data.thingsType == 'all') {
 //若是點擊的是‘所有’,則直接將中間變量中的數據傳給releaseInfo,組件獲取數據進行渲染
      that.setData({
        releaseInfo: that.data.middleArr
      })
    } else {
      let newArr = []
      const val = that.data.thingsType
      //不然遍歷中間變量中的數據,將知足條件的數據傳給組件
      that.data.middleArr.forEach(item => {
        if( item.thingsType == val){
          newArr.push(item)
        }
      })
      that.setData({
        releaseInfo: newArr
      })
    }
  },
複製代碼

加載發佈的信息(雲函數)

思路分析

  • 調用雲函數getRelease,在集合user-release篩選出未刪除數據(獲得一個數組,數組每一項都是一個對象,每一個對象中包含每一發布信息的releaseId和與之對應的發佈者openId);
  • 而後遍歷數組的每一個對象的releaseId字段,根據該字段上的內容和未刪除deleted:false在集合found || lost篩選出對應失物招領或尋物啓事的發佈信息;
  • 在遍歷過程當中,還要根據每條發佈信息上的createBy字段,在集合goodUser篩選出對應的發佈者信息,並將每條發佈者信息放入發佈信息的createBy中;
  • 最後,將每條已經存入發佈者信息的發佈信息unshift進入數組returnResult中(保證將最新的發佈信息渲染在最前面),返回returnResult

數據庫記錄示例

代碼實現

// 雲函數getRelease
exports.main = async (event, context) => {
  let releaseList = await db.collection('user-release')
    .where({
      deleted: false
    })
    .get()
  let returnResult = []
  for (let item of newReleaseList) {
    const oneRelease = await db.collection(event.tabbarText)
    // 調用雲函數傳入 event.tabbarText=found||lost
    // 在found || lost 集合中獲取數據
      .where({
        _id: item.releaseId,
        deleted: false
      })
      .get()
    // 通過上面的代碼篩選,newReleaseList中可能存在空項
    // 能夠直接返回returnResult.push(oneRelease.data)進行查看理解 
    if (oneRelease.data.length > 0) {
      const userInfo = await db.collection('goodUser')
        .where({
          openId: oneRelease.data[0].createBy
        })
        .get()
      oneRelease.data[0].createBy = userInfo.data[0];
      returnResult.unshift(oneRelease.data[0])
    }
  }
  return returnResult
}
複製代碼

效果展現

分頁加載

思路分析

  • 設置每次獲取4條發佈信息、默認當前頁爲1,當用戶下拉觸底時,執行onShow
  • 調用雲函數getRelease再次請求數據,將返回的數據傳遞給組件渲染;
  • 在雲函數getRelease中,將獲取的數組使用slice方法,slice接受的參數爲:- (當前頁 * 每次獲取數) ,用參數newRelease接收該方法返回的新數組。新數組中將是最新的(當前頁 * 每次獲取數)條發佈信息。

代碼實現

// miniprogram/pages/found/found.js
onReachBottom: function () {
    this.setData({
      currentPage: this.data.currentPage + 1
    })
    console.log(this.data.currentPage);
    this.onShow()
  },
wx.cloud.callFunction({
      name: 'getRelease',
      data: {
        tabbarText: that.data.tabbarText,
        currentPage: that.data.currentPage
      },
  ......
複製代碼
// 雲函數 getRelease
let releaseList = await db.collection('user-release')
    .where({
      deleted: false
    })
    .get()
  const currentPage = event.currentPage
  const releaseSize = 4
  const sliceRelease = releaseSize  * currentPage
  const newReleaseList = releaseList.data.slice(-sliceRelease)
  // 在這裏插入實現分頁加載
  let returnResult = []
  for (let item of newReleaseList) {
    const oneRelease = await db.collection(event.tabbarText)
      .where({
........
複製代碼

效果展現

技術總結

  • 組件間的通訊除了可使用properties,也能夠嘗試selectComponent來調用組件上的方法,將數據傳過去。
  • 在實際項目中,總會遇到不少須要按步驟執行的函數,稍不注意,獲取的數據就是錯的,經過使用promise系列方法能夠很好的解決異步問題。
  • 在小程序中頻繁屢次使用setData是很是消耗性能的,在設計js邏輯、構建函數、設置變量的時候要多加考慮項目功能先後的聯繫,從而保證代碼嚴謹而且減少資源消耗。
  • 雲開發能夠很好的幫助咱們同時完成先後端的配置,在開發過程當中加深咱們對後端數據的理解和運用。
相關文章
相關標籤/搜索