前不久,小程序推出了雲開發的功能,使開發者們無需搭建服務器,用雲端能力直接邁入全棧開發。正巧用着網易雲課堂充電,界面精緻、細節到位,因而決定用雲開發來仿一仿網易雲課堂。來,先看一波效果圖
html
|-study163 項目名node
|-cloudfunctions 雲函數 |-getMyCourse |-getCourseInfo |-getCart |-miniprogram 項目模塊 |-components 自定義組件 |-box-module 盒子 |-special-module 專題 |-utils 工具 |-indexMock 獲取主頁數據 |-viewContent 文本處理 |-pages 頁面 |-account 帳號 |-cart 購物車 |-confirm 確認訂單 |-courseInfo 課程信息 |-myStudy 個人學習 |-index 首頁 |-vant-weapp 有贊vant框架組件庫 |-··· app.js 全局js app.json 全局json配置 app.wxss 全局wxss
在新版的開發者工具中,具備了雲開發的功能,且在啓動模板中添加了雲開發快速啓動模板git
雲開發的三個核心github
數據庫 文檔(JSON)數據庫 相似MongoDB
存儲管理 存儲空間,相似百度雲盤
雲函數 獨立的node項目sql
數據庫數據庫
課程的數據項是多出了個人預料的,這裏我只定義了我所使用了的部分。每一個頁面顯示不一樣的課程。這裏要注意的一點是,數據滾動欄裏的、課程盒子裏的、課程信息裏的圖片雖然看起來類似,但並非同一張,因此儲存的連接不一樣。小tips:設計好數據庫後記得設置相對應的權限管理,不然可能出現讀取不到數據的狀況。_id是數據庫自動生成的屬性(惟一),而我定義了一個id屬性,串聯個個數據表(collection),相似傳統sql數據庫中的主鍵。json
存儲管理小程序
當文件儲存在此後,會生成一個下載地址,能夠直接拿出來用。後端
固然也能夠用相應的API直接進行上傳或下載數組
雲函數
雲函數就是部署在雲端的獨立運行的node後端項目,根據自身的業務具體實現。在生成雲函數後,首先記得安裝依賴,在終端中yarn。寫完後不光得保存還得記得上傳並部署。雲函數(小demo)的門檻不高,能夠輕鬆助你實現全棧開發。這裏用最簡單的查詢舉個例子。
// 雲函數入口文件 const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database(); const course_cart = db.collection('course_cart'); // 雲函數入口函數 exports.main = async (event, context) => { let info = course_cart.where({ id: event.id }).get(); return info }
拿到數據後,下一步就是在頁面上顯示出來,衆所周知js是個單線程語言,而數據交互是個異步過程。這一段沒有處理好的話,可能出現即便獲取到了數據也沒顯示出來的問題。因此啊,異步問題仍是得交給promise處理!而當有多處數據交互時,用promise .all()
const promiseArr = cart_coursesId.map(course => new Promise((resolve, reject) =>{ wx.cloud.callFunction({ name: 'getCart', data: { id: course} }) .then(res =>{ resolve(res.result.data[0]); }) })) Promise .all(promiseArr) .then(data => { console.log(data); wx.hideLoading() this.setData({ courses: data }) })
主頁
關於主頁的課程盒子,我想用一個模板兼容全部的表現效果,因此這也是坑我最多的地方。爲了兼容大圖的展示,我給圖片加上了一個isBig屬性(bool)。當isBig爲false時,圖片寬度爲48.7%,margin-right爲 2.6%;,並經過父類的ntd-child(even)將偶數子類(處於右邊)的margin-right改成0%,造成 (48.7%+2.6%)+48.7%的結構分割。但當isBig爲true,第一行就只有一張大圖,因此從第二行開始偶數子元素出如今左邊,結構變成48.7%+(48.7%+2.6%)原本在中間的間隙到了右邊,兩張圖片貼在了一塊兒,很不美觀。因此我在每處理到一張大圖的同時,再生成一個display爲none的小盒子。這樣第一行依然只顯示一張大圖,但有了兩個子元素。因此下面的行列,依然是奇數子類在左邊,偶數子類在右邊。效果實現了,可是當我想把它封裝成組件時,天坑出現了 component不支持ntd-child選擇器,而且組件的slot也並不僅是直白的整段插入。不過往後仍是會從新封裝~
<view class="courses"> <block wx:for="{{mainPage.sellWell.content}}" wx:key="index" > <block wx:if="{{item.isBig}}"> <view class="course_big" > <navigator url="../courseInfo/courseInfo?id={{item.id}}" > <image class="course-image_big " src="{{item.image}}" /> <text class="course-title">{{item.title}}</text> <text class="course-price" wx:if="{{item.price > 0}}">¥{{item.price}}</text> </navigator> </view> <view style="display:none"></view> </block> <block wx:else> <view class="course" > <navigator url="../courseInfo/courseInfo?id={{item.id}}" > <image class="course-image " src="{{item.image}}" /> <text class="course-title">{{item.title}}</text> <text class="course-price" wx:if="{{item.price > 0}}">¥{{item.price}}</text> </navigator> </view> </block> </block> </view>
購物車
相關數據項有
totalPrice:0, //總價 selectedId:[], //被選中的課程ID selectAllStatus: false //全選狀態
經過對列表中數據的isSelected屬性判斷,來計算總價
getTotalPrice: function(){ let courses = this.data.courses; let total = 0; let selectedId = []; for(let i=0;i<courses.length;i++){ if(courses[i].isSelected){ total += courses[i].price; selectedId.push(courses[i].id) } } this.setData({ totalPrice:total, selectedId }) console.log('[total]',total); console.log('[selected]',selectedId); return total; }
選中課程,仍是MVVM數據綁定,且選中後從新計算總價
selectedList:function(e){ const index = e.currentTarget.dataset.index; const courses = this.data.courses; courses[index].isSelected = !courses[index].isSelected; this.setData({ courses }) this.getTotalPrice(); }
全選,每次進行selectAll操做,先將selectAllStatus改成!selectAllStatus,(全選=>全不選 || 全不選=>全選 ),以後將全部數據的isSelected屬性統一爲selectAllStatus的當前狀態。
selectAll:function(e){ let courses = this.data.courses; let selectAllStatus = this.data.selectAllStatus; selectAllStatus = !selectAllStatus; courses.forEach((item, index) => { item.isSelected = selectAllStatus }) this.setData({ courses, selectAllStatus }) this.getTotalPrice(); }
數據傳遞到下一個界面 將選中的數組selectedId做爲對象經過navigator傳入下一個頁面。
<block wx:if="{{totalPrice > 0}}"> <navigator url="../confirm/confirm?ids={{selectedId}}"> <view class="pay-btn active" bindtap="confirm">去結算</view> </navigator> </block>
訂單
先獲取購物車傳遞的數據
let orderIds = options.ids.split(","); console.log(orderIds) this.setData({ userInfo : app.globalData.userInfo, orderIds: orderIds })
完成訂單後,將購買的課程存入數據庫內
wx.cloud.init() const db = wx.cloud.database(); const my_courses = db.collection('my_courses'); addMyCourse(){ const orders = this.data.orders; for(let order of orders){ let myCourse = { title:order.title, image:order.image, id:order.id } console.log(myCourse); my_courses.add({data:myCourse}) } }
提交訂單以後 my_courses更新,相應的,打開被購買的課程頁面也會更新,查詢當前課程是否在購買的課程中,在則將isPaid改成true。
wx.cloud.callFunction({ name: 'getMyCourse', }).then(res=>{ for(const my_course of res.result.data){ if(options.id === my_course.id){ let isPaid = true; this.setData({ isPaid }) return ; } } }) <block wx:if="{{isPaid}}"> <view class="foot">打開網易雲課堂APP學習 支持倍速播放<view class="arrow"></view></view> </block> <block wx:else> </block>
indexMock 此處是用來請求數據的,數據的請求不要放在頁面的js中,將它封裝出去,import到須要用的頁面便可。
let indexMock = function(url){ return new Promise((resolve,reject) => { wx.request({ url:url, success(res){ resolve(res.data) }, fail(err){ reject(err) } }) })
}
module.exports = { indexMock: indexMock }
viewContent
在顯示課程介紹時,遇到了一個頭疼的問題,原來介紹是html格式的,有各類<span><p>
之類的標籤,而小程序內是wxml文件。最初我想到的是用wxparse來解析。但此處我只須要解析輕量的文章,有點殺雞焉用牛刀的感受。其實呢,wxparse的實現就是經過生成node數組來轉換。因此我乾脆直接用正則寫一個轉換的數組工具。
const viewContent = (data) => { if(data && (typeof data === "string")){ return data.replace(/<\/?span>/g, "").replace(/<\/?p>/g, "").replace(/ /g, " ").split("<br>"); } else{ return } } module.exports = { viewContent: viewContent }
數據庫中存的是html文檔
這裏要注意的一點是要給txt-item設置一個高度,實現空行的效果。
寫項目時老是,思考不全老是遇到各類各樣的坑坑坑,算是意識到結構健壯、邏輯一致的重要性。wxml是結構,首先得保證結構的穩定性,至於樣式就是體力活了,至於數據那就是MVVM大顯身手的地方了。路漫漫其修遠兮,吾將上下而求索。
感謝閱讀,文中有錯誤的地方或者有建議歡迎提出!
網易雲課堂 確實是個好地方鴨,沒事多去充充電
項目地址:https://github.com/MarchYuanx...
歡迎 ☆☆☆star☆☆☆!