小兔鲜儿 - 微信登录
涉及知识点:微信授权登录,文件上传,Store 状态管理等。
微信登录
微信小程序的开放能力,允许开发者获取微信用户的基本信息(昵称、性别、手机号码等),开发者常用来实现注册/登录的功能。
登录方式
常见登录/注册方式:
- 用户名/手机号 + 密码
- 手机号 + 验证码
- 授权登录
实际开发过程中常常需要实现以上的一种或多种方式,我们的项目主要实现授权登录。
微信授权登录
用户在使用小程序时,其实已登录微信,其本质上就是:微信授权给小程序读取微信用户信息。

传统登录方式
传统登录方式,一般是通过输入密码或者手机验证码实现登录。

温馨提示:接口文档中提供练习使用的登录接口,大家可在课后自行完成。
静态结构
登录页
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
| // src/pages/login/login.vue
<script setup lang="ts"> // </script>
<template> <view class="viewport"> <view class="logo"> <image src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/logo_icon.png" ></image> </view> <view class="login"> <!-- 网页端表单登录 --> <!-- <input class="input" type="text" placeholder="请输入用户名/手机号码" /> --> <!-- <input class="input" type="text" password placeholder="请输入密码" /> --> <!-- <button class="button phone">登录</button> -->
<!-- 小程序端授权登录 --> <button class="button phone"> <text class="icon icon-phone"></text> 手机号快捷登录 </button> <view class="extra"> <view class="caption"> <text>其他登录方式</text> </view> <view class="options"> <!-- 通用模拟登录 --> <button> <text class="icon icon-phone">模拟快捷登录</text> </button> </view> </view> <view class="tips" >登录/注册即视为你同意《服务条款》和《小兔鲜儿隐私协议》</view > </view> </view> </template>
<style lang="scss"> page { height: 100%; }
.viewport { display: flex; flex-direction: column; height: 100%; padding: 20rpx 40rpx; }
.logo { flex: 1; text-align: center; image { width: 220rpx; height: 220rpx; margin-top: 15vh; } }
.login { display: flex; flex-direction: column; height: 60vh; padding: 40rpx 20rpx 20rpx;
.input { width: 100%; height: 80rpx; font-size: 28rpx; border-radius: 72rpx; border: 1px solid #ddd; padding-left: 30rpx; margin-bottom: 20rpx; }
.button { display: flex; align-items: center; justify-content: center; width: 100%; height: 80rpx; font-size: 28rpx; border-radius: 72rpx; color: #fff; .icon { font-size: 40rpx; margin-right: 6rpx; } }
.phone { background-color: #28bb9c; }
.wechat { background-color: #06c05f; }
.extra { flex: 1; padding: 70rpx 70rpx 0; .caption { width: 440rpx; line-height: 1; border-top: 1rpx solid #ddd; font-size: 26rpx; color: #999; position: relative; text { transform: translate(-40%); background-color: #fff; position: absolute; top: -12rpx; left: 50%; } }
.options { display: flex; justify-content: center; align-items: center; margin-top: 70rpx; button { padding: 0; background-color: transparent; } }
.icon { font-size: 24rpx; color: #444; display: flex; flex-direction: column; align-items: center;
&::before { display: flex; align-items: center; justify-content: center; width: 80rpx; height: 80rpx; margin-bottom: 6rpx; font-size: 40rpx; border: 1rpx solid #444; border-radius: 50%; } } .icon-weixin::before { border-color: #06c05f; color: #06c05f; } } }
.tips { position: absolute; bottom: 80rpx; left: 20rpx; right: 20rpx; font-size: 22rpx; color: #999; text-align: center; } </style>
|
获取登录凭证
前端:调用 wx.login() 接口获取登录凭证(code)。
后端:通过凭证(code)向微信服务器生成用户登录态。
{7}1 2 3 4 5 6 7 8 9 10
| <script setup lang="ts"> import { onLoad } from "@dcloudio/uni-app";
// 获取 code 登录凭证 let code = ""; onLoad(async () => { const res = await wx.login(); code = res.code; }); </script>
|
::: warning 注意
:::
获取手机号码
出于安全限制,小程序【规定】想获取用户的手机号,必须由用户主动【点击按钮】并【允许申请】才可获取加密的手机号信息。
前端:提供 open-type
按钮,在事件处理函数中获取加密的手机号信息。
后端:解密手机号信息,把手机号和用户登录态关联在一起。

参考代码
{3-5,11-14}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script setup lang="ts"> // 获取用户手机号码 const onGetphonenumber: UniHelper.ButtonOnGetphonenumber = (ev) => { console.log(ev); }; </script>
<template> <view class="viewport"> <view class="login"> <button class="button phone" open-type="getPhoneNumber" @getphonenumber="onGetphonenumber" > <text class="icon icon-phone"></text> 手机号快捷登录 </button> </view> </view> </template>
|
::: danger 常见问题
Q:为什么我无法唤起获取手机号的界面?
A:获取手机号功能目前针对非个人开发者,所以个人开发者无法唤起获取手机号界面 详见文档。
:::
::: tip 温馨提示
:::
微信登录接口(生产环境)
获取手机号功能,目前针对非个人开发者,且完成了认证的小程序开放,详见文档。
接口调用
接口地址:/login/wxMin
请求方式:POST
请求参数:
Body
字段名称 |
是否必须 |
默认值 |
备注 |
code |
是 |
无 |
wx.login 获取 |
iv |
是 |
无 |
getphonenumber 事件回调获取 |
encryptedData |
是 |
无 |
getphonenumber 事件回调获取 |
请求封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
import type { LoginResult } from "@/types/member"; import { http } from "@/utils/http";
type LoginParams = { code: string; encryptedData: string; iv: string; };
export const postLoginWxMinAPI = (data: LoginParams) => { return http<LoginResult>({ method: "POST", url: "/login/wxMin", data, }); };
|
类型声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
export type LoginResult = { id: number; avatar: string; account: string; nickname?: string; mobile: string; token: string; };
|
参考代码
小兔鲜儿项目采用常见的 登录凭证 + 手机号 实现授权登录。
{10,15-23,29-32}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| // src/pages/login/login.vue
<script setup lang="ts"> import { postLoginWxMinAPI } from "@/services/login"; import { onLoad } from "@dcloudio/uni-app";
// 获取 code 登录凭证 let code = ""; onLoad(async () => { const res = await wx.login(); code = res.code; });
// 获取用户手机号码 const onGetphonenumber: UniHelper.ButtonOnGetphonenumber = async (ev) => { // 获取参数 const encryptedData = ev.detail.encryptedData!; const iv = ev.detail.iv!; // 登录请求 await postLoginWxMinAPI({ code, encryptedData, iv }); // 成功提示 uni.showToast({ icon: "none", title: "登录成功" }); }; </script>
<template> <view class="viewport"> <view class="login"> <button class="button phone" open-type="getPhoneNumber" @getphonenumber="onGetphonenumber" > <text class="icon icon-phone"></text> 手机号快捷登录 </button> </view> </view> </template>
|
模拟手机登录(开发环境)
获取手机号功能,目前针对非个人开发者,且完成了认证的小程序开放,详见文档。
::: tip 温馨提示
为了更好实现登录后续业务,后端提供内部测试接口,只需要传手机号即可实现快捷登录。
:::
请求接口
接口地址:/login/wxMin/simple
请求方式:POST
请求参数:
Body
字段名称 |
是否必须 |
默认值 |
备注 |
phoneNumber |
是 |
无 |
模拟的手机号 |
该接口跟微信登录接口返回的数据格式是相同的。
请求封装
1 2 3 4 5 6 7 8 9 10 11 12 13
|
export const postLoginWxMinSimpleAPI = (phoneNumber: string) => { return http<LoginResult>({ method: "POST", url: "/login/wxMin/simple", data: { phoneNumber, }, }); };
|
用户信息持久化存储
Pinia 的持久化存储插件在 项目起步 模块已经搭建完成,现在只需要在用户登录成功后,补充 TS 类型声明并保存用户信息即可。
参考代码
Store
{3,12,15}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
import type { LoginResult } from "@/types/member"; import { defineStore } from "pinia"; import { ref } from "vue";
export const useMemberStore = defineStore( "member", () => { const profile = ref<LoginResult>();
const setProfile = (val: LoginResult) => { profile.value = val; };
const clearProfile = () => { profile.value = undefined; };
return { profile, setProfile, clearProfile }; }, { persist: { storage: { getItem(key) { return uni.getStorageSync(key); }, setItem(key, value) { uni.setStorageSync(key, value); }, }, }, } );
|
登录页
{3,30,31}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <script setup lang="ts"> import { postLoginWxMinAPI, postLoginWxMinSimpleAPI } from "@/services/login"; import { useMemberStore } from "@/stores"; import type { LoginResult } from "@/types/member"; import { onLoad } from "@dcloudio/uni-app";
// 获取 code 登录凭证 let code = ""; onLoad(async () => { const res = await wx.login(); code = res.code; });
// 获取用户手机号码(企业中写法) const onGetphonenumber: UniHelper.ButtonOnGetphonenumber = async (ev) => { const encryptedData = ev.detail.encryptedData!; const iv = ev.detail.iv!; const res = await postLoginWxMinAPI({ code, encryptedData, iv }); loginSuccess(res.result); };
// 模拟手机号码快捷登录(开发练习) const onGetphonenumberSimple = async () => { const res = await postLoginWxMinSimpleAPI("13123456789"); loginSuccess(res.result); };
const loginSuccess = (profile: LoginResult) => { // 保存会员信息 const memberStore = useMemberStore(); // [!code ++] memberStore.setProfile(profile); // [!code ++] // 成功提示 uni.showToast({ icon: "success", title: "登录成功" }); setTimeout(() => { // 页面跳转 uni.switchTab({ url: "/pages/my/my" }); }, 500); }; </script>
|
到此,微信登录模块已经完成,接下来进入到用户模块。