實現效果:
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等),在線圖片庫(https://www.superbed.cn/)
2.分包:文件資源過大時,可使用分包加載 (https://www.cnblogs.com/cisum/p/10190245.html)