從頭到尾擼個(gè)疫情期間針對(duì)學(xué)生信息報(bào)備的小程序(微信小程序+云開(kāi)發(fā)實(shí)踐)

前言


初學(xué)小程序,每天起床第一件事不是去看文檔,而是打開(kāi)班群接龍打卡信息,填寫(xiě)ex表(苦逼的大二狗每天群通知99+),所以萌生了寫(xiě)一款讓學(xué)生報(bào)備信息的小程序。本來(lái)只是簡(jiǎn)單寫(xiě)了一個(gè)上報(bào)表單的程序,但是寫(xiě)完了之后又覺(jué)得好像缺了點(diǎn)什么,所以功能越寫(xiě)越多,項(xiàng)目不斷重構(gòu),里面很多東西都是現(xiàn)學(xué)現(xiàn)用,陸陸續(xù)續(xù)寫(xiě)了10天左右,算是一滴都不剩了。。不對(duì),是差不多寫(xiě)得沒(méi)想法了,項(xiàng)目包含了一整套前后端的交互,由于很多數(shù)據(jù)前期和后期設(shè)計(jì)理念不一樣,簡(jiǎn)稱自己打自己臉,所以會(huì)有很多不完善的地方,希望大家輕噴。。。 (* ゜ェ゜ *) 廢話不多說(shuō),現(xiàn)在主要講一下該項(xiàng)目的設(shè)計(jì)思路和一些功能的實(shí)現(xiàn)思路。

技術(shù)棧

  • 微信小程序
  • 云開(kāi)發(fā)
  • vant
  • colorui
  • echarts微信小程序版本

功能設(shè)計(jì)


首頁(yè)

image

首頁(yè)長(zhǎng)這樣,由一個(gè)謠言的輪播(數(shù)據(jù)來(lái)源丁香園)和一個(gè)本校學(xué)生寒假分布地圖等等組成,學(xué)生上報(bào)數(shù)據(jù)之后地圖相應(yīng)的省份的人數(shù)便會(huì)更新。
地圖的數(shù)據(jù)存在云數(shù)據(jù)庫(kù)中,單獨(dú)由一份表來(lái)維護(hù),每個(gè)省份都是一個(gè)記錄。

image

{
  "_id": "上海",  
  "name": "上海", //省名
  "value": 87.0  //該省份存在本校學(xué)生的人數(shù)
}

地圖的數(shù)據(jù)從云函數(shù)getArea獲取后返回到前臺(tái)頁(yè)面,進(jìn)行地圖的初始化,具體的例子可以參考微信小程序版echarts的map,也可以直接看我的源碼,這里說(shuō)一下里面的一些坑,由于繪畫(huà)地圖要引入中國(guó)地圖的json數(shù)據(jù)(目錄下的mapData),而小程序版本的echarts的例子中只有河南地圖的json數(shù)據(jù),因此需要去echarts這里來(lái)復(fù)制中國(guó)地圖的json代碼,粘貼至目錄下的mapData中的json段落,才可以繪制中國(guó)的地圖(其余地圖也同理)。當(dāng)初簡(jiǎn)直被坑得不要不要的。因?yàn)榈貓D數(shù)據(jù)是異步獲取的,所以地圖的初始化在獲取數(shù)據(jù)后進(jìn)行。


    this.ecComponent = this.selectComponent('#mychart-dom-bar');
    wx.cloud.callFunction({
      name: 'getArea'
    }).then((res)=>{
      let result = res.result
      let option = initOption(result)
      this.ecComponent.init((canvas, width, height) => {
        // 獲取組件的 canvas、width、height 后的回調(diào)函數(shù)
        // 在這里初始化圖表
        const chart = echarts.init(canvas, null, {
          width: width,
          height: height
        });
        chart.setOption(option)
        // 將圖表實(shí)例綁定到 this 上,可以在其他成員函數(shù)(如 dispose)中訪問(wèn)
        this.chart = chart;
        return chart;
      });
    })

卡片式的輪播來(lái)自于colorui,這是一個(gè)微信小程序的css庫(kù),將對(duì)應(yīng)的class名稱添加進(jìn)去即可。


數(shù)據(jù)上報(bào)頁(yè)面

image

學(xué)生填寫(xiě)自己的姓名學(xué)號(hào)手機(jī),選擇自己所在的學(xué)院班級(jí)(由于精力有限只做了幾個(gè)學(xué)院),添加自己所在的城市,選擇是否發(fā)熱后就可上報(bào)數(shù)據(jù)。
用戶數(shù)據(jù)存在云數(shù)據(jù)庫(kù)中,單獨(dú)由一份表維護(hù),每個(gè)用戶是一個(gè)記錄

//一份用戶數(shù)據(jù)例子
{
  "passCity": [
    "北京市-北京市-東城區(qū)"                  //目前所在地
  ],
  "openId": "oLuLy5MxC_dnd0eZhDVESsoMRln0", //用戶唯一標(biāo)識(shí)
  "isHot": 1.0,                             //1無(wú)發(fā)熱 2有發(fā)熱
  "admin": true,                            //是否為管理員
  "classId": 1.0,                           //班級(jí)所在id
  "isCommited": 1.0,                        //是否提交過(guò)了
  "name": "馬化騰",                         //名字
  "phone": "18074815679",                   //號(hào)碼
  "studentId": "1233545"                    //學(xué)號(hào)
}

第一次上報(bào)前會(huì)申請(qǐng)獲取用戶的信信息以存入數(shù)據(jù)庫(kù),用戶微信的openId作為用戶的唯一標(biāo)識(shí),如果用戶已經(jīng)存在數(shù)據(jù)庫(kù)了就返回相應(yīng)的用戶數(shù)據(jù),如果不存在則初始化用戶數(shù)據(jù)存入數(shù)據(jù)庫(kù)中。

// login
const cloud = require('wx-server-sdk')
cloud.init({
  // API 調(diào)用都保持和云函數(shù)當(dāng)前所在環(huán)境一致
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database().collection('user')

// 云函數(shù)入口函數(shù)
exports.main = async (event, context) => {
  const {OPENID} = cloud.getWXContext()
  let result = await db.where({
    openId: OPENID
  }).get()
  //如果在數(shù)據(jù)庫(kù)沒(méi)找到該用戶,則初始化數(shù)據(jù)后存入數(shù)據(jù)庫(kù)
  if(!result.data.length){
    Object.assign(event.user, event.userInfo)
    event.user.isPut = false
    event.user.isHot = 0
    event.user.passCity = []
    await db.add({
      data:event.user
    })
  }
  return result
}

用戶填寫(xiě)完信息后會(huì)觸發(fā)兩個(gè)云函數(shù)的調(diào)用,一個(gè)是更新用戶的數(shù)據(jù),將用戶加入相應(yīng)的班級(jí),另一個(gè)是更新地圖數(shù)據(jù),將用戶的所在的省份的學(xué)生人數(shù)加一。

//updateArea
exports.main = async(event, context) => {
  const {
    OPENID
  } = cloud.getWXContext()
  // 如果已經(jīng)提交過(guò)了的學(xué)生再提交的話,就把上一次保存地區(qū)的人數(shù)減1
  if (event.isCommited) {
    let oldCity = await db.collection('user').where({
      openId: event.userInfo.openId
    }).get()
    oldCity = oldCity.data[0].passCity[0].substr(0, 2)
    //因?yàn)閺V西省還有內(nèi)蒙古之類的自治區(qū)的名字是不好控制的,所以使用模糊匹配
    await db.collection('area').where({
      name: db.RegExp({
        regexp: oldCity,
        options: 's',
      })
    }).update({
      data: {
        value: _.inc(-1)
      }
    })
  }
  //因?yàn)閺V西省還有內(nèi)蒙古之類的自治區(qū)的名字是不好控制的,所以使用模糊匹配
  await db.collection('area').where({
    name: db.RegExp({
      regexp: event.citys[0],
      options: 's',
    })
  }).update({
    data: {
      value: _.inc(1)
    }
  })
  return 'ok'
}
///updataUser
exports.main = async (event, context) => {
  const {
    OPENID
  } = cloud.getWXContext()
  const { name, phone, citys, isHot, studentId, classId} = event
  //第一次提交時(shí)會(huì)提交所屬的班級(jí),將該學(xué)生的信息存到相應(yīng)班級(jí)的表中
  if (classId){
    await db.collection('class').where({
      classId
    }).update({
      data:{
        commitedStudents:_.push({
          name,
          phone,
          citys,
          isHot: isHot - 0
        })
      }
    })
  }
  await db.collection('user').where({
    openId: OPENID
  }).update({
      data: {
        name,
        studentId,
        phone,
        classId,
        passCity: citys,
        isHot: isHot - 0,
        isCommited:1,
        isPut:true}
  })
  return 'ok'
}

管理員頁(yè)面

image

管理員頁(yè)面可以查看全校哪位學(xué)生在湖北,哪位學(xué)生有發(fā)熱跡象,也可以查看某個(gè)班級(jí)的提交情況,班級(jí)學(xué)生列表。進(jìn)入此頁(yè)面需要權(quán)限驗(yàn)證,即用戶的openId對(duì)應(yīng)的記錄下有admin:true字段,需要開(kāi)發(fā)者手動(dòng)在數(shù)據(jù)庫(kù)中用戶添加此字段即可授予管理員權(quán)限。


image
//驗(yàn)證權(quán)限云函數(shù)
const cloud = require('wx-server-sdk')

cloud.init({
  // API 調(diào)用都保持和云函數(shù)當(dāng)前所在環(huán)境一致
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()
// 云函數(shù)入口函數(shù)
exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  const OPENID = wxContext.OPENID || event.OPENID
  let user = await db.collection('user').where({
    openId: OPENID
  }).get()
  user = user.data[0]
  if(!user.admin){
    return 'error'
  }
  return 'ok'
}

班級(jí)數(shù)據(jù)存在云數(shù)據(jù)庫(kù)中,單獨(dú)由一份表維護(hù),每個(gè)班級(jí)是一個(gè)記錄


{
  "classId": 9,                   //班級(jí)id
  "name": "16經(jīng)濟(jì)一",             //班級(jí)名字
  "student_sum": 50,              //班級(jí)總?cè)藬?shù)
  "commitedStudents": []          //已經(jīng)提交信息了的學(xué)生,每個(gè)學(xué)生是個(gè)對(duì)象
}

如何啟動(dòng)本項(xiàng)目

  • git clone git@github.com:Akakiiiiii/students-system.git
  • cd students-system
  • cd cloudfunctions
  • npm i
  • 使用微信開(kāi)發(fā)工具導(dǎo)入該小程序,填寫(xiě)自己appId
  • 打開(kāi)項(xiàng)目后進(jìn)入云開(kāi)發(fā)->數(shù)據(jù)庫(kù),創(chuàng)造三個(gè)表,分別是area,user,class,并分別導(dǎo)入項(xiàng)目json文件夾下的json文件,area表單導(dǎo)入名字帶area的json文件,以此類推。(init代表僅僅初始化表單,沒(méi)有數(shù)據(jù)。沒(méi)有init的就是有數(shù)據(jù)的,假設(shè)你想看效果就導(dǎo)入名字不帶init的)
  • 最后在微信開(kāi)發(fā)者工具上傳所有云函數(shù)即可,選擇云端安裝依賴,即可跑起該項(xiàng)目。

完整項(xiàng)目請(qǐng)查看

github地址https://github.com/Akakiiiiii/students-system
如果對(duì)您有幫助,希望可以得到一枚您的Star~。(〃'▽'〃)
有任何可以改進(jìn)的地方希望您可以花費(fèi)一些時(shí)間開(kāi)啟一個(gè)Issue或者直接PR~。φ(>ω<)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容