前不久,小程序推出了雲開發的功能,使開發者們無需搭建服務器,用雲端能力直接邁入全棧開發。正巧用着網易雲課堂充電,界面精緻、細節到位,因而決定用雲開發來仿一仿網易雲課堂。javascript
來,先看一波效果圖html
購物車java
直接購買node
在項目開發中選用好的工具使得工做事半功倍git
|-study163 項目名
|-cloudfunctions 雲函數
|-getMyCourse 獲取個人課程
|-getCourseInfo 獲取課程信息
|-getCart 獲取購物車
|-miniprogram 項目模塊
|-components 自定義組件
|-box-module 盒子
|-myCourse-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
複製代碼
在新版的開發者工具中,具備了雲開發的功能,且在啓動模板中添加了雲開發快速啓動模板github
雲開發的三個核心
sql
課程的數據項是多出了個人預料的,這裏我只定義了我所使用了的部分。每一個頁面顯示不一樣的課程。這裏要注意的一點是,數據滾動欄裏的、課程盒子裏的、課程信息裏的圖片雖然看起來類似,但並非同一張,因此儲存的連接不一樣。小tips:設計好數據庫後記得設置相對應的權限管理,不然可能出現讀取不到數據的狀況。數據庫
_id是數據庫自動生成的屬性(惟一),而我定義了一個id屬性,串聯個個數據表(collection),相似傳統sql數據庫中的主鍵。json
course_info小程序
course_cart
my_course
當文件儲存在此後,會生成一個下載地址,能夠直接拿出來用。
固然也能夠用相應的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處理!
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爲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>複製代碼
雖然簡單,但這是典型的數據驅動界面效果,數據綁定,也就是MVVM。頁面經過對數據status的判斷來決定顯示對應的部分。
<ul class="status">
<li>
<text class="{{status == '1' ? 'active':''}}" bindtap="showStatus" data-status="1">個人課程</text>
</li>
<li>
<text class="{{status == '2' ? 'active':''}}" bindtap="showStatus" data-status="2">個人微專業</text>
</li>
</ul>
<block wx:if="{{status == '1'}}">
</block>
<block wx:else>
</block>複製代碼
給相應的標籤設置對應的data-status,再將修改的函數綁定到bindtap上,一個最簡單的MVVM例子就實現了。
showStatus: function(e){
let status = e.currentTarget.dataset.status;
this.setData({
status:status
})
}複製代碼
關於課程的獲取就是雲函數的用武之地了
onShow:function(){
wx.cloud.callFunction({
name: 'getMyCourse',
}).then(res=>{
this.setData({
my_courses: res.result.data
})
})
}複製代碼
在你須要使用雲函數的地方調用wx.cloud.callFunction(),把要調用的雲函數的函數名做爲參數傳入,如此處{name: 'getMyCourse'}。接着在wxml內經過一個wx:for把數據輸出便可。此處myCourse-module爲封裝好了的自定義組件。
<view wx:for="{{my_courses}}" wx:key="index">
<myCourse-module image="{{item.image}}" title="{{item.title}}" cid="{{item.id}}">
</myCourse-module>
</view>複製代碼
相關數據項有
totalPrice:0,
selectedId:[],
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>複製代碼
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}複製代碼
import { indexMock } from '../../utils/indexMock.js';
//先引入indexMock
Page({
data: {
mainPage:{},
url:"https://www.easy-mock.com/mock/5bda9d1a58caf84108172bab/study163/mainPage",
},
onLoad() {
const url = this.data.url;
indexMock(url) //indexMock返回的是一個Promise 後面用then()處理就
.then(res => {
console.log(res)
this.setData({
mainPage:res
})
})
.then(res=>{
console.log("mainpage",this.data.mainPage)
})
} //其餘函數....
});
複製代碼
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/study163
歡迎 ☆☆☆star☆☆☆!