1、课程介绍

0基础入门微信小程序开发理解微信小程序的开发流程理解小程序云开发的使用独立完成小程序全栈项目

htmlcssJavaScript

多敲代码多看官方文档

基础内容 > 注册申请 > 开发工具

代码构成 > JSON > WXML > WXSS > JS

云开发 > 云数据库 > 云函数 > 云存储

电影案例 > 上线审核

电影小程序
用户登录如何通过云函数获取openid
传统微信登录 VS 云开发微信登录
如何获取用户信息
电影列表如何云函数调用第三方API
云函数调用API VS 小程序调用API
渲染列表
电影评价云数据库插入数据
选择相册图片或拍照
云存储的图片上传

2、小程序基础

2-1、小程序注册

mp.weixin.qq.com

2-2、小程序开发工具介绍

developers.weixin.qq.com

2-3、创建小程序及代码结构介绍

.json:配置文件,以json格式存储一些配置

.wxml:模板文件,描述页面结构,相当于HTML

.wxss:样式文件,调整页面样式,相当于css

.js:脚本逻辑文件,页面和用户的交互逻辑

2-4、配置文件JSON

project.config.json:项目配置

app.json:全局配置

page.json:页面配置

2-5、页面结构WXML

WXML 全程是WeiXin MarkUp Language,是小程序框架设计的一套标签语言,结合小程序的基础组件、事件系统,可以构建出页面的结构,充当的就是类似HTML的角色。

数据从动态的服务端获取,渲染到页面

数据绑定使用 Mustache 语法(双大括号)将变量包起来

wx:for="{{list}}"

wx:if="{{isLogin}}"、很长时间不改变的场景

hidden="{{!isLogin}}"、频繁切换场景

base.wxml
<view>Hi {{msg}}</view>
<view wx:for="{{arr}}" wx:key="*this">{{index}} {{item}}</view>
<view wx:for="{{list}}" wx:key="*this">{{item.name}} {{item.age}}</view>
<view>
    <view wx:if="{{isLogin}}">Bob已登录</view>
    <view wx:else>请登录</view>
    <view hidden="{{!isLogin}}">hidden</view>
</view>
base.js
Page({
    data: {
        msg: 'Vue',
        arr:['a','b'],
        list:[
            {
                name:'bob1',
                age:19
            },{
                name:'bob2',
                age:29
            }
        ],
        isLogin:true
    }
}) 

2-6、页面样式WXSS

WXSS (WeiXin Style Sheets)是一套用于小程序的样式语言,用于描述WXML的组件样式,也就是视觉上的效果。

尺寸单位:rpx(responsive pixel):可以根据屏幕宽度进行自适应,适配不同宽度的屏幕。

引入外部wxss:@import './test.wxss'

第三方样式库:WeUIiView WeappVant Weapp

2-7、页面交互JS

JS负责逻辑交互

计数器demo

this.setData({
  count:this.data.count+1
})

事件是对用户的交互操作行为的相应

bind VS catch

事件对象

base.wxml
<view class="box" catchtap="onbox" data-id='onbox父'>
    <view class="child" catchtap="onchild"></view>
</view>
<view> 
    <button bindtap="handle">点我+1</button>
    <view>{{count}}</view>
</view>
base.js
Page({
    data: {
        count:0
    },
    handle:function(){
        this.setData({
            count:this.data.count+1
        })
    },
    onbox:function(e){
        console.log('onbox 父');
        console.log(e);
        console.log(e.currentTarget.dataset.id);

    },
    onchild:function(e){
        console.log('onchild 儿');
        console.log(e);
    },
}) 

3、小程序云开发

3-1、小程序云开发介绍

云函数 云数据库 云存储

image-20210814192014113 image-20210814192556789 image-20210814192742217 image-20210814192948307

3-2、云数据库

image-20210814194321142 image-20210814194631337 image-20210814194704365

image-20210814194757634

数据库初始化

初始化 const db = wx.cloud.database()

切换环境 const testDB = wx.cloud.database({ env:'test' })

通过小程序控制,插入数据会默认插入openid、通过控制台插入不会默认插入openid

cloud.wxml
<button type="warn">小程序控制</button>
<button type="primary" bindtap="insert">增加数据</button>
<button type="primary" bindtap="update">更新数据</button>
<button type="primary" bindtap="search">查找数据</button>
<button type="primary" bindtap="delete">删除数据</button>
cloud.js
// 初始化数据库
const db = wx.cloud.database();

Page({
    data: {

    },
    insert: function () {
      
        // 普通写法
        /*
        db.collection('userss').add({
            data: {
                name: 'bob',
                age: 20
            },
            success: res => {
                console.log(res);
            },
            fail: err => {
                console.log(err);
            }
        })
        */
      
        // Promise写法
        db.collection('userss').add({
            data: {
                name: 'bob',
                age: 20
            }
        }).then(res=>{
            console.log(res);
        }).catch(err=>{
            console.log(err);
        })
        
    },
    update:function(){
        db.collection('userss').doc('8937eaa96117afc104b0a4b6145d825c')
        .update({
            data:{
                age:210
            }
        }).then(res=>{
            console.log(res);
        }).catch(err=>{
            console.log(err);
        })
    },
    search:function(){
        db.collection('userss').where({
            name:'bob'
        }).get().then(res=>{
            console.log(res);
        }).catch(err=>{
            console.log(err);
        })
    },
    delete:function(){
        db.collection('userss').doc('8937eaa96117b09304b0d7d23131e328')
        .remove().then(res=>{
            console.log(res);
        }).catch(err=>{
            console.log(err);
        })
    },
})

3-3、云函数

求和函数 sum()

获取当前用户的openid

批量删除云数据库的数据

上传并部署:云端安装依赖(不上传node_modules)

云函数更新一次,就要右键上传并部署一次

cloud.wxml
<button type="warn">云函数控制</button>
<button type="primary" bindtap="sum">调用云函数sum</button>
<button type="primary" bindtap="getOpenId">获取当前用户openid</button>
<button type="primary" bindtap="batchDelete">批量删除</button>
cloud.js
// 初始化数据库
const db = wx.cloud.database();

Page({
    data: {

    },
    sum: function () {
        wx.cloud.callFunction({
            name: 'quickstartFunctions',
            config: {
                env: this.data.envId
            },
            data: {
                type: 'sum',
                a:2,
                b:3
            }
        }).then(res=>{
            console.log(res);
        }).catch(err=>{
            console.log(err);
        })
    },
    getOpenId:function(){
        wx.cloud.callFunction({
            name: 'quickstartFunctions',
            config: {
                env: this.data.envId
            },
            data: {
                type: 'getOpenId'
            }
        }).then(res=>{
            console.log(res);
        }).catch(err=>{
            console.log(err);
        })
    },
    batchDelete:function(){
        wx.cloud.callFunction({
            name: 'quickstartFunctions',
            config: {
                env: this.data.envId
            },
            data: {
                type: 'batchDelete'
            }
        }).then(res=>{
            console.log(res);
        }).catch(err=>{
            console.log(err);
        })
    },
  
})
sum index.js
const cloud = require('wx-server-sdk')

cloud.init({
    env: cloud.DYNAMIC_CURRENT_ENV
})

// 获取openId云函数入口函数
exports.main = async (event, context) => {
    return {
        sum: event.a + event.b
    }
}
getOpenid index.js
const cloud = require('wx-server-sdk')

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

// 获取openId云函数入口函数
exports.main = async (event, context) => {
  // 获取基础信息
  const wxContext = cloud.getWXContext()

  return {
    openid: wxContext.OPENID,
    appid: wxContext.APPID,
    unionid: wxContext.UNIONID,
  }
}
batchDelete index.js
const cloud = require('wx-server-sdk')

cloud.init({
    env: cloud.DYNAMIC_CURRENT_ENV
})

const db = cloud.database();


// 获取openId云函数入口函数
exports.main = async (event, context) => {
    try {
        return await db.collection('userss').where({
            name: 'bob'
        }).remove();
    } catch (e) {
        console.error(e);
    }
}

3-4、云存储(1)

image-20210814215333413 image-20210814220235422 image-20210814225012484

3-5、云存储(2)

cloud.wxml
<button type="warn">云存储</button>
<button type="primary" bindtap="upload">上传图片</button>
<button type="primary" bindtap="getFile">文件展示</button>
<block wx:for="{{imgs}}" wx:key="*this">
    <image src="{{item.fileID}}"></image>
    <button type="primary" 
    data-fileID="{{item.fileID}}" 
    bindtap="downloadFile">文件下载</button>
</block>
cloud.js
// 初始化数据库
const db = wx.cloud.database();

Page({
    data: {
        imgs:[]
    },
    upload: function () {
        // 选择图片
        wx.chooseImage({
            count: 1,
            sizeType: ['original', 'compressed'],
            sourceType: ['album', 'camera'],
            success(res) {
                // tempFilePath可以作为img标签的src属性显示图片
                const tempFilePaths = res.tempFilePaths;
                console.log(tempFilePaths);
                // 
                wx.cloud.uploadFile({
                    // 指定上传到的云路径
                    cloudPath: new Date().getTime()+'.png',
                    // 指定要上传的文件的小程序临时文件路径
                    filePath: tempFilePaths[0],
                    // 成功回调
                    success: res => {
                        // console.log('上传成功', res)
                        console.log('上传成功', res.fileID)
                        // 
                        db.collection('demoImg').add({
                            data:{
                                fileID:res.fileID
                            }
                        }).then(res=>{
                            console.log(res);
                        }).catch(err=>{
                            console.log(err);
                        })
                    },
                    fail:console.error
                })
            }
        })

    },
    getFile:function(){
        wx.cloud.callFunction({
            name: 'quickstartFunctions',
            data: {
                type: 'getOpenId'
            }
        }).then(res => {
            console.log(res.result.openid);
            db.collection('demoImg').where({
                _openid:res.result.openid
            }).get().then(res2=>{
                console.log(res2.data);
                this.setData({
                    imgs:res2.data
                })
            })
        }).catch(err => {
            console.log(err);
        })
    },
    downloadFile:function(e){
        console.log(e);
        wx.cloud.downloadFile({
            fileID: e.target.dataset.fileid, // 文件 ID
            success: res => {
              // 返回临时文件路径
              console.log(res.tempFilePath);
              // 保存图片到手机相册
              wx.saveImageToPhotosAlbum({
                filePath:res.tempFilePath,
                success(res) { 
                    wx.showToast({
                        title:'保存成功'
                    })
                }
              })
            },
            fail: console.error
          })
    }
})

4、电影小程序案例

4-1、功能介绍与环境搭建

developers.weixin.qq.com

mp.weixin.qq.com

iconfont.cn

4-2、Vant组件库

https://youzan.github.io/vant-weapp

1、在miniprogram目录下 npm init

2、通过 npm 安装 npm i @vant/weapp -S --production

3、工具 > 构建npm

4、详情 > 本地设置 > 使用npm模块

5、"usingComponents": { "van-button": "@vant/weapp/button" }

6、<van-button type='danger'>危险按钮</van-button>

4-3、电影列表

image-20210815142012130

http://github.com

https://github.com/request/request-promise

在对应云函数文件夹里安装 npm install --save request
继续 npm install --save request-promise

package.json
{
  "name": "quickstartFunctions",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "request": "^2.88.2",
    "request-promise": "^4.2.6",
    "wx-server-sdk": "~2.4.0"
  }
}
movielist.js
const cloud = require('wx-server-sdk')

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

// 电影列表 http://api/douban.com/v2/movie/in_theaters
// 电影详情 http://api/douban.com/v2/movie/subject/id
/*
https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=10&page_start=10

https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=10&page_start=10

page_limit 是一次请求数
page_start 是从第几条开始重新请求

start 是从第几条开始重新请求
count 是一次请求数


http://api.douban.com/v2/movie/subject/${event.movieid}?apikey=0df993c66c0c636e29ecbb5344252a4a

`http://api.douban.com/v2/movie/subject/${event.movieid}?apikey=0df993c66c0c636e29ecbb5344252a4a`
*/
var rp = require('request-promise');

exports.main = async (event, context) => {

  return rp(`https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=${event.count}&page_start=${event.start}`)
    .then(function (res) {
      console.log(res);
      return res
    })
    .catch(function (err) {
      console.err(err);
    });

}
movie.wxml
<view class='movie' wx:for="{{movieList}}" wx:key="{{index}}">
  <image class='movie-img' src=" {{item.cover}}">
  </image>
  <view class="movie-info">
    <view class="movie-title">{{item.title}}</view>
    <view class="">观众评分:
      <text class="movie-score">{{item.rate}}分</text>
    </view>
    <button bindtap="gotoComment" data-movieid="{{item.id}}">评价</button>
    <!-- <view>
      <text wx:for="{{item.casts}}">{{item.name}} </text>
    </view>
    <view>年份:{{item.year}}</view> -->
  </view>
</view>
movie.js
Page({
  data: {
    movieList: []
  },
  getMovieList:function() {
    wx.showLoading({
      title: '加载中',
    })
    wx.cloud.callFunction({
      name: 'quickstartFunctions',
      data: {
        type: 'movielist',
        start: this.data.movieList.length, // 第一次0-9 第二次 10-19 开始的地方 page_start 
        count: 10 // page_limit    
      }
    }).then(res => {
      console.log(res);
      this.setData({
        // this.data.movieList是数组,在原有数组拼接新的
        movieList: this.data.movieList.concat(JSON.parse(res.result).subjects)
      })
      wx.hideLoading();
    }).catch(err => {
      console.err(err);
      wx.hideLoading();
    })
  },
  gotoComment:function(e){
    console.log(e.target.dataset.movieid);
    wx.navigateTo({
      url: `../comment/comment?movieid=${e.target.dataset.movieid}`,
    })
  },
  onLoad: function (e) {
    this.getMovieList();
  },
  onReachBottom:function(){
    this.getMovieList();
  }
  
})

4-4、电影详情

4-5、电影评价

4-6、用户信息

<view class="bg1">
    <view class="userinfo">
        <block wx:if="{{!hasUserInfo}}">
            <button wx:if="{{canIUseGetUserProfile}}" bindtap="getUserProfile">
                点击登录
            </button>
        </block>
        <block wx:else>
            <view class="userinfo">
                <!-- 用户头像 -->
                <view class="userinfo-avatar">
                    <image src="{{userInfo.avatarUrl}}" mode="widthFix" class="png"></image>
                </view>
                <!-- 用户名称 -->
                <view class="userinfo-NickName">
                    {{userInfo.nickName}}
                </view>
            </view>
        </block>
    </view>
</view>


<view class="userinfo">
    <!-- 用户头像 -->
    <view class="userinfo-avatar">
        <open-data type='userAvatarUrl'></open-data>
    </view>
    <!-- 用户名称 -->
    <view class="userinfo-NickName">
        <open-data type='userNickName'></open-data>
    </view>
</view>
.bg1 {
  border-bottom: 10rpx solid #ccc;
}
/*  */
.userinfo {
  font-size: 14px;
  border-radius: 40%;
}

.userinfo-avatar {
  overflow: hidden;
  display: block;
  width: 100rpx;
  height: 100rpx;
  margin-top: 32rpx;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);
}

.userinfo-NickName {
  margin: -50rpx 0rpx 0rpx 150rpx;
  color: #aaa;
}

/*  */
.userinfo {
  position: relative;
  width: 750rpx;
  height: 320rpx;
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
}
 
.userinfo-avatar {
  overflow:hidden;
  display: block;
  width: 160rpx;
  height: 160rpx;
  margin: 20rpx;
  margin-top: 50rpx;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);
}
 
.userinfo text {
  color: #fff;
  font-size: 14px;
  background-color: #c0c0c0;
  border-radius:40%;
}
const app = getApp();
const db = wx.cloud.database();
const _ = db.command;
var that;
wx.cloud.init()

// 在页面中定义插屏广告对象
var interstitialAd = null;


Page({
  data: {
    hasRegistered: false, //true是update,false是add
    openid: '',
    userInfo: [],
    hasUserInfo: false,
    canIUseGetUserProfile: true,
  },
  onLogin:function(){ // 开发者如需获取用户身份标识符只需要调用wx.login接口即可
    wx.login({
      timeout: 0,
      success:res=>{
        console.log(res);
      }
    })
  },
  getUserProfile(e) {
    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
    // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    wx.getUserProfile({
      desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
        console.log(res);
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        });
        // 判断,获取userInfo,存入storage,新增或者更新时使用
        wx.setStorageSync('userInfo', res.userInfo);
      },
      fail: function (res) { //用户点了“拒绝”
        wx.showModal({
          title: '登陆后获取更多功能',
          content: "点击确定重新登陆",
          cancelText: "取消",
          confirmText: "确定",
          success: function (res) {
            if (res.cancel) {
              //点击取消,默认隐藏弹框
              console.log("no");
            } else {
              //点击确定
              console.log("ok");
              wx.getUserProfile({
                desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
                success: (res) => {
                  console.log(res);
                  that.setData({
                    userInfo: res.userInfo,
                    hasUserInfo: true
                  });
                  // 判断,获取userInfo,存入storage,新增或者更新时使用
                  wx.setStorageSync('userInfo', res.userInfo);
                },
              })
            }
          }
        })
      }
    })
    // 已注册,更新用户信息;没注册,新增用户
    let that = this
    if (that.data.hasRegistered) {
      // true是update,false是add
      // console.log("updateUser");
      this.updateUser()
    } else {
      // true是update,false是add
      // console.log("addUser");
      this.addUser()
    }
    // 
  },
  getUserInfo(e) {
    // 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
    this.setData({
      userInfo: e.detail.userInfo,
      hasUserInfo: true
    })
  },
  onLoad: function () {
    
    if (wx.getUserProfile) {
      this.setData({
        canIUseGetUserProfile: true
      })
    }
    //Load过程中,判断用户是否已经注册
    let that = this
    // 调用login云函数获取openid,存入storage
    wx.cloud.callFunction({
      name: 'login',
      data: {},
      success: res => {
        app.globalData.openid = res.result.openid;
        wx.setStorageSync('openid', res.result.openid);
        var openid = wx.getStorageSync('openid');
        // console.log(openid);
      },
      fail: err => {
        console.error('[云函数] [login] 调用失败', err);
      }
    })
    //使用缓存中的openid判断是否已经注册过,返回值 > 0说明有已有数据,注册过,直接调用update方法
    db.collection('user').where({
      _openid: wx.getStorageSync('openid')
    }).get({
      success(res) {
        // console.log(res.data);
        console.log(res.data.length);
        if (res.data.length > 0) {
          that.setData({
            hasRegistered: true //将hasRegistered更新为true是update 
          })
        }
      }
    })
  },
  //新增用户,并将hasReigstered设置为true
  addUser() {
    var userInfo = {
      name: wx.getStorageSync('userInfo').nickName,
      avatarUrl: wx.getStorageSync('userInfo').avatarUrl,
      gender: wx.getStorageSync('userInfo').gender,
      punchsNumber: 0,
      shareNumber: 0,
      likesNumber: 0,
      start: new Date()
    }
    let that = this
    db.collection('user').add({
      data: userInfo,
      success(res) {
        that.setData({
          hasRegistered: true
        })
        console.log("新增用户成功", res)
      }
    })
  },
  //更新用户数据
  updateUser() {
    var userInfo = {
      name: wx.getStorageSync('userInfo').nickName,
      avatarUrl: wx.getStorageSync('userInfo').avatarUrl,
      gender: wx.getStorageSync('userInfo').gender
    }
    db.collection('user').where({
      _openid: wx.getStorageSync('openid')
    }).update({
      data: userInfo,
      success(res) {
        console.log("更新用户信息成功", res)
      }
    })
  },
})

4-7、审核上线

版本号、项目备注、提交审核、确定上线

5、课程回顾

小程序基础知识 > 小程序云开发 > 电影小程序案例

关掉视频,独立完成案例

反复看小程序官方文档

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐