小程序相关
实现效果:
1.开发工具uniapp
2.功能模块
① 登录模块(微信登录,授权获取昵称,头像,地区等,授权获取手机号,解密手机号)
② UI模块 (uview,vant)
③ 请求模块 (axios封装)
④ 问题汇总
3.功能拆解(附代码)
① 微信登录:文档(https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html)
步骤:
<1> 通过微信官方提供的登录api(wx.login)获取code,用code当参数请求后台接口获取openid,以此作为登录凭证
<2> 分段代码:
wx.login({
success(res) {
if (res.code) {
//向后台发起网络请求
wx.request({
url: '后台提供的接口地址',
data: {
code: res.code
},
success(res) {
console.log("获取到的openid是:", res)
}
})
} else {
console.log('登录失败!' + res.errMsg)
}
}
})
② 授权获取昵称,头像,地区:文档(https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html)
步骤:
<1> 页面产生点击事件(例如 button
上 bindtap
的回调中)后才可调用
<2> 分段代码:
wx.getUserProfile({
lang: 'zh_CN',
desc: '用户登录',
success: (res) => {
console.log("用户允许授权后获得的信息为:", res)
},
fail: (err) => {
console.log("用户拒绝授权")
}
})
③ 授权获取手机号及解密,文档(https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html)
步骤:
<1> 将 button 组件 open-type
的值设置为 getPhoneNumber
<2> 用户同意后,通过 bindgetphonenumber
事件回调获取到微信服务器返回的加密数据
<3> 获取session_key
<4> 向后台发送参数解密手机号
<5> 分段代码:
// html代码:
// 原生微信小程序写法:
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
// uniapp写法:
<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">获取手机号码</button>
// js代码:
// 获取手机号:
getPhoneNumber(e) {
if (e.detail.errMsg !== "getPhoneNumber:ok") return; // 如果用户不同意授权则返回并结束
console.log(e.detail.errMsg)
console.log(e.detail.iv)
console.log(e.detail.encryptedData)
}
// 检验登录是否过期:
uni.checkSession({
success(res) {
console.log("登录未过期,进行下一步操作")
},
fail() {
console.log("登录已过期,请重新登录")
}
})
// 获取session_key,以此作为参数进行解密手机号
wx.request({
url: "https://api.weixin.qq.com/sns/jscode2session",
data: {
'appid': "小程序的appid",
'secret': "小程序密钥",
'js_code': "登录时获取到的code",
'grant_type': "authorization_code" // 默认填写authorization_code
},
method: 'GET',
header: {
'content-type': 'application/json'
},
success: function(res) {
console.log("成功获取到session_key", res)
},
fail(err) {
console.log("获取session_key失败", err)
}
})
// 解密手机号
wx.request({
url: '后台提供的接口地址',
data: {
'encryptedData': e.detail.encryptedData,
'iv': e.detail.iv,
'session_key': "上一步获取到的session_key"
},
method: 'GET',
header: {
'content-type': 'application/json'
},
success: function(data) {
console.log("获取到的手机号是", data)
},
fail: function(err) {
console.log(err)
}
})
④ 微信登录流程的完整代码:
<template>
<!-- #ifdef MP-WEIXIN -->
<view v-if="!isCanUse">
<view>
<view class='header'>
<image src='你的网站logo等等'></image>
</view>
<view class='content'>
<view>申请获取以下权限</view>
<text>获得你的公开信息(昵称,头像、地区等)</text>
</view>
<button v-if="!flag" type="primary" class='btn' withCredentials="true" lang="zh_CN" open-type="getUserInfo" @tap="wxLogin">
授权登录
</button>
<button v-else type="primary" class='btn' open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">获取手机号码</button>
</view>
</view>
<!-- #endif -->
</template>
<script>
export default {
data() {
return {
SessionKey: '',
OpenId: '',
code: '',
isCanUse: uni.getStorageSync('isCanUse'), //默认为true
token: '',
userInfo: {
nickName: null,
avatarUrl: null,
},
flag: false,
toWeb: false,
webSrc: 'http://localhost:8080/#/?token=' + uni.getStorageSync('token'),
}
},
onLoad() {
if (uni.getStorageSync('isCanUse') == '1') {
console.log('已经授权登录过了,请直接跳转')
} else {
console.log('未授权登录,请点击登录')
}
},
methods: {
wxLogin(e) {
let p1 = this.wxSilentLogin() // 获取code
let p2 = this.wxGetUserProfile() // 获取用户信息
p1.then((code) => {
return code
})
.then((code) => {
this.code = code
return new Promise((resolve, reject) => {
p2.then((res) => {
resolve({
code,
iv: res.iv,
encryptedData: res.encryptedData,
})
}).catch((err) => {
reject(err)
})
})
})
.then((res) => {
let _this = this
// 请求服务器
wx.request({
url: '后台提供的接口',
method: 'get',
data: {
code: res.code,
encrypted_data: res.encryptedData,
iv: res.iv,
},
header: {
'content-type': 'application/json', // 默认值
},
success(res) {
uni.setStorageSync('token', res.data.data)
_this.token = res.data.data
console.log('获取token', _this.token)
},
})
})
.catch((err) => {
console.log(err)
})
},
wxGetUserProfile: function () {
// 获取头像昵称等
let _this = this
return new Promise((resolve, reject) => {
wx.getUserProfile({
lang: 'zh_CN',
desc: '用户登录',
success: (res) => {
resolve(res)
console.log('获取到的用户信息', res.userInfo)
_this.userInfo.nickName = res.userInfo.nickName
_this.userInfo.avatarUrl = res.userInfo.avatarUrl
uni.setStorageSync('isCanUse', '1')
_this.flag = true
// _this.updateUserInfo();
},
// 失败回调
fail: (err) => {
reject(err)
console.log('选择了拒绝')
},
})
})
},
wxSilentLogin: function () {
// 获取code
return new Promise((resolve, reject) => {
wx.login({
success(res) {
resolve(res.code)
console.log('获取得到的loginres', res)
},
fail(err) {
reject(err)
},
})
})
},
getPhoneNumber: function (e) {
var self = this
if (e.detail.errMsg !== 'getPhoneNumber:ok') return
wx.showLoading()
uni.checkSession({
success(res) {
if (self.code) {
// 2.访问登录凭证校验接口获取session_key
wx.request({
url: 'https://api.weixin.qq.com/sns/jscode2session',
data: {
appid: '小程序appid',
secret: '小程序密钥',
js_code: self.code,
grant_type: 'authorization_code',
},
method: 'GET',
header: {
'content-type': 'application/json',
},
success: function (data) {
console.log('获取到session_key啦', data)
if (data.statusCode == 200) {
//3. 解密
wx.request({
url: '后台提供的接口',
data: {
encryptedData: e.detail.encryptedData,
iv: e.detail.iv,
sessionKey: data.data.session_key,
},
method: 'GET',
header: {
'content-type': 'application/json',
},
success: function (data) {
wx.hideLoading()
console.log('获取到的手机号是', data.data.phoneNumber)
},
fail: function (err) {
console.log(err)
},
})
}
},
fail: function (err) {
console.log(err)
},
})
} else {
wx.showToast({
icon: 'none',
title: '授权失败,请重新授权',
})
self.flag = false
}
},
fail() {
wx.showToast({
icon: 'none',
title: '登录过期,请重新登录',
})
},
})
},
updateUserInfo: function () {
return new Promise((resolve, reject) => {
console.log('发送给后台的用户信息', this.userInfo)
wx.request({
url: '后台提供的接口',
method: 'POST',
data: {
openId: '',
password: '',
telePhone: '',
tenantId: 0,
username: '',
},
header: {
'content-type': 'application/json', // 默认值
},
success(res) {
console.log(333, res.data)
},
})
wx.login({
success(res) {
resolve(res.code)
},
fail(err) {
reject(err)
},
})
})
},
},
}
</script>
<style>
.header {
margin: 90rpx 0 90rpx 50rpx;
border-bottom: 1px solid #ccc;
text-align: center;
width: 650rpx;
height: 300rpx;
line-height: 450rpx;
}
.header image {
width: 240rpx;
height: 240rpx;
border-radius: 50%;
}
.content {
margin-left: 50rpx;
margin-bottom: 90rpx;
}
.content text {
display: block;
color: #9d9d9d;
margin-top: 40rpx;
}
.btn {
background: #1890FF !important;
line-height: 80rpx !important;
border-radius: 80rpx !important;
margin: 70rpx 50rpx !important;
font-size: 35rpx !important;
}
</style>
注:1. background: url 无法使用静态资源,可以使用线上资源或者数据流(base64等),在线图片库
2.分包:文件资源过大时,可使用分包加载 ()