样式修改
在登陆页添加微信登陆
<Form.Item>
其他登陆方式:<Button onClick={handleWeixinLogin}>微信</Button>
</Form.Item>
扫码页面
// 使用微信扫码登陆
const handleWeixinLogin = async() => {
const origin: string = window.location.origin;
window.location.href= `http://gmall-h5-api.atguigu.cn/api/user/weixin/login?redirect_uri=${origin}`
}
后端会根据redirect_uri,当用户扫码登陆一下,会跳转到login页面,在页面参数中会携带token,
登陆流程处理
最后,在withAuthorization
中统一处理登陆逻辑。
import { FC } from "react";
import { useLocation, Navigate } from "react-router-dom";
import { Spin } from "antd";
import { useAppDispatch, useAppSelector } from "@/app/hooks";
import { getUserInfoAsync, selectUser, setToken } from "@/pages/login/slice";
/*
高阶组件HOC
本质上是一个函数,接受组件作为参数,返回一个新组件
WrappedComponent 组件是哪个?
看路由路径(地址)
/login --> EmptyLayout/Login
/syt/dashboard --> Layout/Dashboard
*/
function withAuthorization(WrappedComponent: FC) { // FunctionComponent
return () => {
// 读取redux中管理的token和用户名 ==> 只要状态数据发生了改变, 当前组件函数就会自动重新执行
const { token, name } = useAppSelector(selectUser);
// 获取当前请求的路由地址
const { pathname } = useLocation();
const dispatch = useAppDispatch();
// 如果有token, 说明至少登录过
if (token) {
...
} else { // 没有登录过 => 都得去登陆页面
// 判断是否是微信扫码登陆
const params = new URLSearchParams(document.location.search.substring(1));
const token = params.get('token');
if (token) {
// 微信扫码登陆,获取到token,直接跳转到首页
dispatch(setToken(token))
return <Navigate to="/syt/dashboard" />;
}
// 如果访问的是登陆页面, 直接显示对应的组件
if (pathname === "/login") {
return <WrappedComponent />;
}
// // 如果访问不是登录页面, 自动跳转到登陆页面
return <Navigate to="/login" />;
}
};
}
export default withAuthorization;
在store中定义一个setToken方法,用来保存微信扫码后获取到的token
// 创建当前redux模块的管理对象slice
const userSlice = createSlice({
name: 'user', // 标识名称
initialState, // 初始状态
// 配置同步action对应的reducer => 同步action会自动生成
reducers: {
setToken: (state, action) => {
// 用来保存微信扫码登陆的token
const token = action.payload;
state.token = token
localStorage.setItem('token_key', token)
}
},
})
export const { setToken } = userSlice.actions;
尚品汇扫码登陆功能实现代码分支:https://github.com/zxfjd3g/gshop-client/tree/feature/wx-login