本文主要是介绍React+Redux+Ant Design+TypeScript 电子商务实战-客户端应用 02 登录注册,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
创建导航菜单
// src\components\core\Navigation.tsx
import { Menu } from 'antd'
import { Link } from 'react-router-dom'
import { useSelector } from 'react-redux'
import { AppState } from '../../store/reducers'
import { RouterState } from 'connected-react-router'// 判断选中类名的钩子函数
function useActive(currentPath: string, path: string): string {return currentPath === path ? 'ant-menu-item-selected' : ''
}const Navigation = () => {const router = useSelector<AppState, RouterState>(state => state.router)const pathname = router.location.pathnameconst isHome = useActive(pathname, '/')const isShop = useActive(pathname, '/shop')return (<Menu mode="horizontal" selectable={false}><Menu.Item className={isHome}><Link to="/">首页</Link></Menu.Item><Menu.Item className={isShop}><Link to="/shop">商城</Link></Menu.Item></Menu>)
}export default Navigation
// src\components\core\Layout.tsx
import React, { FC } from 'react'
import Navigation from './Navigation'// 定义 Layout 组件参数类型的接口
interface Props {children: React.ReactNode
}// FC 表示函数型组件类型
const Layout: FC<Props> = ({ children }) => {return (<div><Navigation></Navigation><div style={{ width: '85%', minWidth: '980px', margin: '0 auto' }}>{children}</div></div>)
}export default Layout
创建页头
页头组件
// src\components\core\Layout.tsx
import { PageHeader } from 'antd'
import React, { FC } from 'react'
import Navigation from './Navigation'// 定义 Layout 组件参数类型的接口
interface Props {children: React.ReactNodetitle: stringsubTitle: string
}// FC 表示函数型组件类型
const Layout: FC<Props> = ({ children, title, subTitle }) => {return (<div><Navigation></Navigation><PageHeader className="jumbotron" title={title} subTitle={subTitle} /><div style={{ width: '85%', minWidth: '980px', margin: '0 auto' }}>{children}</div></div>)
}export default Layout
页头样式
/* src\style.css */
.jumbotron {background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);background-size: 400% 400%;-webkit-animation: Gradient 15s ease infinite;-moz-animation: Gradient 15s ease infinite;animation: Gradient 15s ease infinite;margin-bottom: 25px;
}.jumbotron span {color: #fff;
}@-webkit-keyframes Gradient {0% {background-position: 0% 50%;}50% {background-position: 100% 50%;}100% {background-position: 0% 50%;}
}@-moz-keyframes Gradient {0% {background-position: 0% 50%;}50% {background-position: 100% 50%;}100% {background-position: 0% 50%;}
}@keyframes Gradient {0% {background-position: 0% 50%;}50% {background-position: 100% 50%;}100% {background-position: 0% 50%;}
}
引入样式表
// src\index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import 'antd/dist/antd.css'
import './style.css'
import Routes from './Routes'
import { Provider } from 'react-redux'
import store, { history } from './store'
import { ConnectedRouter } from 'connected-react-router'ReactDOM.render(<React.StrictMode><Provider store={store}><ConnectedRouter history={history}><Routes /></ConnectedRouter></Provider></React.StrictMode>,document.getElementById('root')
)
页面组件传递 title 和 subTitle
// src\components\core\Home.tsx
import { useSelector } from 'react-redux'
import Layout from './Layout'const Home = () => {const state = useSelector(state => state)return (<Layout title="RM商城" subTitle="优享品质 惊喜价格">Home {JSON.stringify(state)}</Layout>)
}export default Home
// src\components\core\Shop.tsx
import { useSelector } from 'react-redux'
import Layout from './Layout'const Shop = () => {const state = useSelector(state => state)return (<Layout title="RM商城" subTitle="挑选你喜欢的商品把">Shop {JSON.stringify(state)}</Layout>)
}export default Shop
构建注册和登录表单
注册页面组件
// src\components\core\Signup.tsx
import Layout from './Layout'
import { Button, Form, Input } from 'antd'const Signup = () => {return (<Layout title="注册" subTitle="还没有账号?注册一个吧"><Form><Form.Item name="name" label="昵称"><Input /></Form.Item><Form.Item name="password" label="密码"><Input.Password /></Form.Item><Form.Item name="email" label="邮箱"><Input /></Form.Item><Form.Item><Button type="primary" htmlType="submit">注册</Button></Form.Item></Form></Layout>)
}export default Signup
登录页面组件
// src\components\core\Signin.tsx
import Layout from './Layout'
import { Button, Form, Input } from 'antd'const Signin = () => {return (<Layout title="登录" subTitle=""><Form><Form.Item name="email" label="邮箱"><Input /></Form.Item><Form.Item name="password" label="密码"><Input.Password /></Form.Item><Form.Item><Button type="primary" htmlType="submit">登录</Button></Form.Item></Form></Layout>)
}export default Signin
配置路由
// src\Routes.tsx
import { HashRouter, Route, Switch } from 'react-router-dom'
import Home from './components/core/Home'
import Shop from './components/core/Shop'
import Signin from './components/core/Signin'
import Signup from './components/core/Signup'const Routes = () => {return (<HashRouter><Switch><Route path="/" component={Home} exact /><Route path="/shop" component={Shop} /><Route path="/signin" component={Signin} /><Route path="/signup" component={Signup} /></Switch></HashRouter>)
}export default Routes
配置导航菜单
// src\components\core\Navigation.tsx
import { Menu } from 'antd'
import { Link } from 'react-router-dom'
import { useSelector } from 'react-redux'
import { AppState } from '../../store/reducers'
import { RouterState } from 'connected-react-router'// 判断选中类名的钩子函数
function useActive(currentPath: string, path: string): string {return currentPath === path ? 'ant-menu-item-selected' : ''
}const Navigation = () => {const router = useSelector<AppState, RouterState>(state => state.router)const pathname = router.location.pathnameconst isHome = useActive(pathname, '/')const isShop = useActive(pathname, '/shop')const isSignin = useActive(pathname, '/signin')const isSignup = useActive(pathname, '/signup')return (<Menu mode="horizontal" selectable={false}><Menu.Item className={isHome}><Link to="/">首页</Link></Menu.Item><Menu.Item className={isShop}><Link to="/shop">商城</Link></Menu.Item><Menu.Item className={isSignin}><Link to="/signin">登录</Link></Menu.Item><Menu.Item className={isSignup}><Link to="/signup">注册</Link></Menu.Item></Menu>)
}export default Navigation
实现注册的 Redux 流程
和注册相关的 action
// src\store\actions\auth.action.ts
// action.type 常量
export const SIGNUP = 'SIGNUP' // 发送注册请求
export const SIGNUP_SUCCESS = 'SIGNUP_SUCCESS' // 注册成功
export const SIGNUP_FAIL = 'SIGNUP_FAIL' // 注册失败// action.payload 接口类型
export interface SignupPayload {email: stringname: stringpassword: string
}// action 对象接口类型
export interface SignupAction {type: typeof SIGNUPpayload: SignupPayload
}
export interface SignupSuccessAction {type: typeof SIGNUP_SUCCESS
}
export interface SignupFailAction {type: typeof SIGNUP_FAILmessage: string
}// actionCreator
export const signup = (payload: SignupPayload): SignupAction => ({type: SIGNUP,payload
})
export const signupSuccess = (): SignupSuccessAction => ({type: SIGNUP_SUCCESS
})
export const signupFail = (message: string): SignupFailAction => ({type: SIGNUP_FAIL,message
})// action 的联合类型
export type AuthUnionType = SignupAction | SignupSuccessAction | SignupFailAction
定义 reducer
// src\store\reducers\auth.reducer.ts
import { AuthUnionType, SIGNUP, SIGNUP_FAIL, SIGNUP_SUCCESS } from '../actions/auth.action'// state 接口类型
export interface AuthState {signup: {loaded: booleansuccess: booleanmessage: string}
}// state 默认值
const initialState: AuthState = {signup: {loaded: false, // 注册请求是否结束success: false, // 注册是否成功message: '' // 注册失败提示}
}export default function authReducer(state = initialState, action: AuthUnionType) {switch (action.type) {// 发送注册请求case SIGNUP:return {...state,signup: {loaded: false,success: false}}// 注册成功case SIGNUP_SUCCESS:return {...state,signup: {loaded: true,success: true}}// 注册失败case SIGNUP_FAIL:return {...state,signup: {loaded: true,success: false,message: action.message}}default:return state}
}
// src\store\reducers\index.ts
import { connectRouter, RouterState } from 'connected-react-router'
import { History } from 'history'
import { combineReducers } from 'redux'
import authReducer, { AuthState } from './auth.reducer'
// import testReducer from './test.reducer'// 定义一个包含 router 的 store 类型接口 供外部使用
export interface AppState {router: RouterStateauth: AuthState
}const createRootReducer = (history: History) =>combineReducers({// test: testReducer,router: connectRouter(history),auth: authReducer})export default createRootReducer
定义 saga 接收请求
// src\store\sagas\auth.saga.ts
import axios from 'axios'
import { takeEvery, put } from 'redux-saga/effects'
import { API } from '../../config'
import { SIGNUP, SignupAction, signupFail, signupSuccess } from '../actions/auth.action'function* handleSignup(action: SignupAction) {try {const response = yield axios.post(`${API}/signup`, action.payload)yield put(signupSuccess())} catch (error) {yield put(signupFail(error.response.data.errors[0]))}
}export default function* authSaga() {yield takeEvery(SIGNUP, handleSignup)
}
// src\store\sagas\index.ts
import { all } from 'redux-saga/effects'
import authSaga from './auth.saga'export default function* rootSaga() {yield all([authSaga()])
}
// src\store\index.ts
import { applyMiddleware, createStore } from 'redux'
import createRootReducer from './reducers'
import { createHashHistory } from 'history'
import { routerMiddleware } from 'connected-react-router'
import { composeWithDevTools } from 'redux-devtools-extension'
import createSagaMiddleware from '@redux-saga/core'
import rootSaga from './sagas'export const history = createHashHistory()const sagaMiddleware = createSagaMiddleware()const store = createStore(createRootReducer(history),composeWithDevTools(applyMiddleware(routerMiddleware(history), sagaMiddleware))
)sagaMiddleware.run(rootSaga)export default store
修改页面组件
// src\components\core\Signup.tsx
import Layout from './Layout'
import { Button, Form, Input } from 'antd'
import { signup, SignupPayload } from '../../store/actions/auth.action'
import { useDispatch } from 'react-redux'const Signup = () => {// 获取 dispatch 方法const dispatch = useDispatch()// 注册表单提交const onFinish = (value: SignupPayload) => {// 发送注册请求dispatch(signup(value))}return (<Layout title="注册" subTitle="还没有账号?注册一个吧"><Form onFinish={onFinish}><Form.Item name="name" label="昵称"><Input /></Form.Item><Form.Item name="password" label="密码"><Input.Password /></Form.Item><Form.Item name="email" label="邮箱"><Input /></Form.Item><Form.Item><Button type="primary" htmlType="submit">注册</Button></Form.Item></Form></Layout>)
}export default Signup
处理注册结果
实现内容
- 注册成功 清空表单
- 注册成功 显示成功的提示信息
- 注册失败 显示失败的提示信息
- 离开页面之前 重置状态
- 注册状态如果未重置,返回该页面仍然会显示注册结果和提示
刷新表单和显示提示信息
// src\components\core\Signup.tsx
import Layout from './Layout'
import { Button, Form, Input, Result } from 'antd'
import { signup, SignupPayload } from '../../store/actions/auth.action'
import { useDispatch, useSelector } from 'react-redux'
import { AppState } from '../../store/reducers'
import { AuthState } from '../../store/reducers/auth.reducer'
import { useEffect } from 'react'
import { Link } from 'react-router-dom'const Signup = () => {// 获取 dispatch 方法const dispatch = useDispatch()// 获取注册结果const auth = useSelector<AppState, AuthState>(state => state.auth)// 创建表单数据域// 用于绑定到 Form 组件上操作表单const [form] = Form.useForm()// 注册表单提交const onFinish = (value: SignupPayload) => {// 发送注册请求dispatch(signup(value))}// 监听状态useEffect(() => {// 1. 注册成功 清空表单if (auth.signup.loaded && auth.signup.success) {form.resetFields()}}, [auth])// 2. 注册成功 显示成功的提示信息const showSuccess = () => {if (auth.signup.loaded && auth.signup.success) {return (<Resultstatus="success"title="注册成功"extra={[<Button type="primary"><Link to="/signin">登录</Link></Button>]}/>)}}// 3. 注册失败 显示失败的提示信息const showError = () => {if (auth.signup.loaded && !auth.signup.success) {return <Result status="warning" title="注册失败" subTitle={auth.signup.message} />}}// 4. 离开页面之前 重置页面状态useEffect(() => {// 离开页面时会执行这个方法return () => {// 稍后写}}, [])// 注册表单const signupForm = () => (<Form form={form} onFinish={onFinish}><Form.Item name="name" label="昵称"><Input /></Form.Item><Form.Item name="password" label="密码"><Input.Password /></Form.Item><Form.Item name="email" label="邮箱"><Input /></Form.Item><Form.Item><Button type="primary" htmlType="submit">注册</Button></Form.Item></Form>)return (<Layout title="注册" subTitle="还没有账号?注册一个吧">{showSuccess()}{showError()}{signupForm()}</Layout>)
}export default Signup
重置注册状态
定义 action
// src\store\actions\auth.action.ts
// action.type 常量
export const SIGNUP = 'SIGNUP' // 发送注册请求
export const SIGNUP_SUCCESS = 'SIGNUP_SUCCESS' // 注册成功
export const SIGNUP_FAIL = 'SIGNUP_FAIL' // 注册失败
export const RESET_SIGNUP = 'RESET_SIGNUP' // 重置注册状态// action.payload 接口类型
export interface SignupPayload {email: stringname: stringpassword: string
}// action 对象接口类型
export interface SignupAction {type: typeof SIGNUPpayload: SignupPayload
}
export interface SignupSuccessAction {type: typeof SIGNUP_SUCCESS
}
export interface SignupFailAction {type: typeof SIGNUP_FAILmessage: string
}
export interface ResetSignupAction {type: typeof RESET_SIGNUP
}// actionCreator
export const signup = (payload: SignupPayload): SignupAction => ({type: SIGNUP,payload
})
export const signupSuccess = (): SignupSuccessAction => ({type: SIGNUP_SUCCESS
})
export const signupFail = (message: string): SignupFailAction => ({type: SIGNUP_FAIL,message
})
export const resetSignup = (): ResetSignupAction => ({type: RESET_SIGNUP
})// action 的联合类型
export type AuthUnionType = SignupAction | SignupSuccessAction | SignupFailAction | ResetSignupAction
定义 reducer
// src\store\reducers\auth.reducer.ts
import { AuthUnionType, RESET_SIGNUP, SIGNUP, SIGNUP_FAIL, SIGNUP_SUCCESS } from '../actions/auth.action'// state 接口类型
export interface AuthState {signup: {loaded: booleansuccess: booleanmessage: string}
}// state 默认值
const initialState: AuthState = {signup: {loaded: false, // 注册请求是否结束success: false, // 注册是否成功message: '' // 注册失败提示}
}export default function authReducer(state = initialState, action: AuthUnionType) {switch (action.type) {// 发送注册请求case SIGNUP:return {...state,signup: {loaded: false,success: false}}// 注册成功case SIGNUP_SUCCESS:return {...state,signup: {loaded: true,success: true}}// 注册失败case SIGNUP_FAIL:return {...state,signup: {loaded: true,success: false,message: action.message}}// 重置注册状态case RESET_SIGNUP:return {...state,signup: {loaded: false,success: false,message: ''}}default:return state}
}
执行重置操作
// src\components\core\Signup.tsx
import Layout from './Layout'
import { Button, Form, Input, Result } from 'antd'
import { resetSignup, signup, SignupPayload } from '../../store/actions/auth.action'
import { useDispatch, useSelector } from 'react-redux'
import { AppState } from '../../store/reducers'
import { AuthState } from '../../store/reducers/auth.reducer'
import { useEffect } from 'react'
import { Link } from 'react-router-dom'const Signup = () => {// 获取 dispatch 方法const dispatch = useDispatch()// 获取注册结果const auth = useSelector<AppState, AuthState>(state => state.auth)// 创建表单数据域// 用于绑定到 Form 组件上操作表单const [form] = Form.useForm()// 注册表单提交const onFinish = (value: SignupPayload) => {// 发送注册请求dispatch(signup(value))}// 监听状态useEffect(() => {// 1. 注册成功 清空表单if (auth.signup.loaded && auth.signup.success) {form.resetFields()}}, [auth])// 2. 注册成功 显示成功的提示信息const showSuccess = () => {if (auth.signup.loaded && auth.signup.success) {return (<Resultstatus="success"title="注册成功"extra={[<Button type="primary"><Link to="/signin">登录</Link></Button>]}/>)}}// 3. 注册失败 显示失败的提示信息const showError = () => {if (auth.signup.loaded && !auth.signup.success) {return <Result status="warning" title="注册失败" subTitle={auth.signup.message} />}}// 4. 离开页面之前 重置页面状态useEffect(() => {// 离开页面时会执行这个方法return () => {dispatch(resetSignup())}}, [])// 注册表单const signupForm = () => (<Form form={form} onFinish={onFinish}><Form.Item name="name" label="昵称"><Input /></Form.Item><Form.Item name="password" label="密码"><Input.Password /></Form.Item><Form.Item name="email" label="邮箱"><Input /></Form.Item><Form.Item><Button type="primary" htmlType="submit">注册</Button></Form.Item></Form>)return (<Layout title="注册" subTitle="还没有账号?注册一个吧">{showSuccess()}{showError()}{signupForm()}</Layout>)
}export default Signup
实现登录的 Redux 流程
和登录相关的 action
// src\store\actions\auth.action.ts/*** 注册*/.../*** 登录*/export const SIGNIN = 'SIGNIN' // 发送登录请求
export const SIGNIN_SUCCESS = 'SIGNIN_SUCCESS' // 登录成功
export const SIGNIN_FAIL = 'SIGNIN_FAIL' // 登录失败export interface SigninPayload {email: stringpassword: string
}export interface SigninAction {type: typeof SIGNINpayload: SigninPayload
}export interface SigninSuccessAction {type: typeof SIGNIN_SUCCESS
}export interface SigninFailAction {type: typeof SIGNIN_FAILmessage: string
}export const signin = (payload: SigninPayload): SigninAction => ({type: SIGNIN,payload
})export const signinSuccess = (): SigninSuccessAction => ({type: SIGNIN_SUCCESS
})export const signinFail = (message: string): SigninFailAction => ({type: SIGNIN_FAIL,message
})// action 的联合类型
export type AuthUnionType =| SignupAction| SignupSuccessAction| SignupFailAction| ResetSignupAction| SigninAction| SigninSuccessAction| SigninFailAction
定义 reducer
// src\store\reducers\auth.reducer.ts
import {AuthUnionType,RESET_SIGNUP,SIGNIN,SIGNIN_FAIL,SIGNIN_SUCCESS,SIGNUP,SIGNUP_FAIL,SIGNUP_SUCCESS
} from '../actions/auth.action'// state 接口类型
export interface AuthState {signup: {loaded: booleansuccess: booleanmessage: string}signin: {loaded: booleansuccess: booleanmessage: string}
}// state 默认值
const initialState: AuthState = {signup: {loaded: false, // 注册请求是否结束success: false, // 注册是否成功message: '' // 注册失败提示},signin: {loaded: false, // 登录请求是否结束success: false, // 登录是否成功message: '' // 登录失败提示}
}export default function authReducer(state = initialState, action: AuthUnionType) {switch (action.type) {...// 发送登录请求case SIGNIN:return {...state,signin: {loaded: false,success: false,message: ''}}// 登录成功case SIGNIN_SUCCESS:return {...state,signin: {loaded: true,success: true,message: ''}}// 登录失败case SIGNIN_FAIL:return {...state,signin: {loaded: true,success: false,message: action.message}}default:return state}
}
定义 sage 接收请求
// src\store\sagas\auth.saga.ts
import axios, { AxiosResponse } from 'axios'
import { takeEvery, put } from 'redux-saga/effects'
import { API } from '../../config'
import {SIGNIN,SigninAction,signinFail,signinSuccess,SIGNUP,SignupAction,signupFail,signupSuccess
} from '../actions/auth.action'function* handleSignup(action: SignupAction) {try {yield axios.post(`${API}/signup`, action.payload)yield put(signupSuccess())} catch (error) {yield put(signupFail(error.response.data.errors[0]))}
}function* handleSignin(action: SigninAction) {try {const response: AxiosResponse = yield axios.post(`${API}/signin`, action.payload)// 存储令牌localStorage.setItem('jwt', JSON.stringify(response.data))yield put(signinSuccess())} catch (error) {yield put(signinFail(error.response.data.errors[0]))}
}export default function* authSaga() {// 注册yield takeEvery(SIGNUP, handleSignup)// 登录yield takeEvery(SIGNIN, handleSignin)
}
修改页面组件
// src\components\core\Signin.tsx
import Layout from './Layout'
import { Button, Form, Input } from 'antd'
import { signin, SigninPayload } from '../../store/actions/auth.action'
import { useDispatch } from 'react-redux'const Signin = () => {// 获取 dispatchconst dispatch = useDispatch()const onFinish = (value: SigninPayload) => {dispatch(signin(value))}return (<Layout title="登录" subTitle=""><Form onFinish={onFinish}><Form.Item name="email" label="邮箱"><Input /></Form.Item><Form.Item name="password" label="密码"><Input.Password /></Form.Item><Form.Item><Button type="primary" htmlType="submit">登录</Button></Form.Item></Form></Layout>)
}export default Signin
处理登录结果
实现内容
- 获取登录结果
- 登录失败 显示错误信息
- 登录成功 根据角色跳转到对应的管理页面
- 处理导航链接
- 已登录
- 隐藏[登录,注册]
- 显示[dashboard]
获取登录结果和显示失败信息
// src\components\core\Signin.tsx
import Layout from './Layout'
import { Button, Form, Input, Result } from 'antd'
import { signin, SigninPayload } from '../../store/actions/auth.action'
import { useDispatch, useSelector } from 'react-redux'
import { AppState } from '../../store/reducers'
import { AuthState } from '../../store/reducers/auth.reducer'const Signin = () => {// 获取 dispatchconst dispatch = useDispatch()const onFinish = (value: SigninPayload) => {dispatch(signin(value))}// 1. 获取登录结果const auth = useSelector<AppState, AuthState>(state => state.auth)// 2. 登录失败 显示错误信息const showError = () => {if (auth.signin.loaded && !auth.signin.success) {return <Result status="warning" title="登录失败" subTitle={auth.signin.message} />}}// 3. 登录成功 根据角色跳转到对应的管理页面// 4. 处理导航链接: 已登录 隐藏[登录,注册] 显示[dashboard]// 登录表单const singinForm = () => (<Form onFinish={onFinish}><Form.Item name="email" label="邮箱"><Input /></Form.Item><Form.Item name="password" label="密码"><Input.Password /></Form.Item><Form.Item><Button type="primary" htmlType="submit">登录</Button></Form.Item></Form>)return (<Layout title="登录" subTitle="">{showError()}{singinForm()}</Layout>)
}export default Signin
登录成功跳转管理页面
定义一个判断是否登录的方法
首先定义一个 User 接口和 Jwt 接口便于其它地方使用:
// src\store\models\auth.ts
export interface User {_id: stringname: stringemail: stringrole: number
}export interface Jwt {token: stringuser: User
}
定义判断方法:
// src\helpers\auth.ts
import { Jwt } from '../store/models/auth'// 是否登录
export function isAuth(): boolean | Jwt {const jwt = localStorage.getItem('jwt')if (jwt) {return JSON.parse(jwt)}return false
}
跳转页面
// src\components\core\Signin.tsx
import Layout from './Layout'
import { Button, Form, Input, Result } from 'antd'
import { signin, SigninPayload } from '../../store/actions/auth.action'
import { useDispatch, useSelector } from 'react-redux'
import { AppState } from '../../store/reducers'
import { AuthState } from '../../store/reducers/auth.reducer'
import { isAuth } from '../../helpers/auth'
import { Jwt } from '../../store/models/auth'
import { Redirect } from 'react-router-dom'const Signin = () => {// 获取 dispatchconst dispatch = useDispatch()const onFinish = (value: SigninPayload) => {dispatch(signin(value))}// 1. 获取登录结果const auth = useSelector<AppState, AuthState>(state => state.auth)// 2. 登录失败 显示错误信息const showError = () => {if (auth.signin.loaded && !auth.signin.success) {return <Result status="warning" title="登录失败" subTitle={auth.signin.message} />}}// 3. 登录成功 根据角色跳转到对应的管理页面const redirectToDashboard = () => {const auth = isAuth()if (auth) {const {user: { role }} = auth as Jwtif (role === 0) {// 普通用户return <Redirect to="/user/dashboard" />} else {// 管理员return <Redirect to="/admin/dashboard" />}}}// 4. 处理导航链接: 已登录 隐藏[登录,注册] 显示[dashboard]// 登录表单const singinForm = () => (<Form onFinish={onFinish}><Form.Item name="email" label="邮箱"><Input /></Form.Item><Form.Item name="password" label="密码"><Input.Password /></Form.Item><Form.Item><Button type="primary" htmlType="submit">登录</Button></Form.Item></Form>)return (<Layout title="登录" subTitle="">{showError()}{redirectToDashboard()}{singinForm()}</Layout>)
}export default Signin
处理导航链接
// src\components\core\Navigation.tsx
import { Menu } from 'antd'
import { Link } from 'react-router-dom'
import { useSelector } from 'react-redux'
import { AppState } from '../../store/reducers'
import { RouterState } from 'connected-react-router'
import { isAuth } from '../../helpers/auth'
import { Jwt } from '../../store/models/auth'// 判断选中类名的钩子函数
function useActive(currentPath: string, path: string): string {return currentPath === path ? 'ant-menu-item-selected' : ''
}const Navigation = () => {const router = useSelector<AppState, RouterState>(state => state.router)const pathname = router.location.pathnameconst isHome = useActive(pathname, '/')const isShop = useActive(pathname, '/shop')const isSignin = useActive(pathname, '/signin')const isSignup = useActive(pathname, '/signup')const isDashboard = useActive(pathname, getDashboardUrl())function getDashboardUrl() {let url = '/user/dashboard'if (isAuth()) {const {user: { role }} = isAuth() as Jwtif (role === 1) {url = '/admin/dashboard'}}return url}return (<Menu mode="horizontal" selectable={false}><Menu.Item className={isHome}><Link to="/">首页</Link></Menu.Item><Menu.Item className={isShop}><Link to="/shop">商城</Link></Menu.Item>{!isAuth() && (<><Menu.Item className={isSignin}><Link to="/signin">登录</Link></Menu.Item><Menu.Item className={isSignup}><Link to="/signup">注册</Link></Menu.Item></>)}{isAuth() && (<Menu.Item className={isDashboard}><Link to={getDashboardUrl()}>dashboard</Link></Menu.Item>)}</Menu>)
}export default Navigation
创建 Dashboard 用户管理页面
创建普通用户管理页面
// src\components\admin\Dashboard.tsx
import Layout from '../core/Layout'const Dashboard = () => {return (<Layout title="用户 dashboard" subTitle="">Dashboard</Layout>)
}export default Dashboard
配置路由
// src\Routes.tsx
import { HashRouter, Route, Switch } from 'react-router-dom'
import Dashboard from './components/admin/Dashboard'
import Home from './components/core/Home'
import Shop from './components/core/Shop'
import Signin from './components/core/Signin'
import Signup from './components/core/Signup'const Routes = () => {return (<HashRouter><Switch><Route path="/" component={Home} exact /><Route path="/shop" component={Shop} /><Route path="/signin" component={Signin} /><Route path="/signup" component={Signup} /><Route path="/user/dashboard" component={Dashboard} /></Switch></HashRouter>)
}export default Routes
创建受保护的路由
dashboard 页面需要登陆后才能访问,要添加访问权限。
创建一个受保护的路由组件,限制只有登录后才能访问的页面:
// src\components\admin\PrivateRoute.tsx
import { Redirect, Route, RouteProps } from 'react-router-dom'
import { FC } from 'react'
import { isAuth } from '../../helpers/auth'interface PrivateRouteProps extends RouteProps {component: React.ComponentType<any>
}const PrivateRoute: FC<PrivateRouteProps> = ({ component: Component, ...rest }) => {return (<Route{...rest}render={props => {const auth = isAuth()if (auth) {return <Component {...props} />}return <Redirect to="/signin" />}}/>)
}export default PrivateRoute
替换原来的路由:
// src\Routes.tsx
import { HashRouter, Route, Switch } from 'react-router-dom'
import Dashboard from './components/admin/Dashboard'
import PrivateRoute from './components/admin/PrivateRoute'
import Home from './components/core/Home'
import Shop from './components/core/Shop'
import Signin from './components/core/Signin'
import Signup from './components/core/Signup'const Routes = () => {return (<HashRouter><Switch><Route path="/" component={Home} exact /><Route path="/shop" component={Shop} /><Route path="/signin" component={Signin} /><Route path="/signup" component={Signup} /><PrivateRoute path="/user/dashboard" component={Dashboard} /></Switch></HashRouter>)
}export default Routes
创建管理员管理页面
// src\components\admin\AdminDashboard.tsx
import Layout from '../core/Layout'const AdminDashboard = () => {return (<Layout title="管理员 dashboard" subTitle="">Dashboard</Layout>)
}export default AdminDashboard
创建管理员身份判断路由
// src\components\admin\AdminRoute.tsx
import { Redirect, Route, RouteProps } from 'react-router-dom'
import { FC } from 'react'
import { isAuth } from '../../helpers/auth'
import { Jwt } from '../../store/models/auth'interface PrivateRouteProps extends RouteProps {component: React.ComponentType<any>
}const AdminRoute: FC<PrivateRouteProps> = ({ component: Component, ...rest }) => {return (<Route{...rest}render={props => {const auth = isAuth() as Jwtif (auth && auth.user.role === 1) {return <Component {...props} />}return <Redirect to="/signin" />}}/>)
}export default AdminRoute
配置管理员管理页面路由
// src\Routes.tsx
import { HashRouter, Route, Switch } from 'react-router-dom'
import AdminDashboard from './components/admin/AdminDashboard'
import AdminRoute from './components/admin/AdminRoute'
import Dashboard from './components/admin/Dashboard'
import PrivateRoute from './components/admin/PrivateRoute'
import Home from './components/core/Home'
import Shop from './components/core/Shop'
import Signin from './components/core/Signin'
import Signup from './components/core/Signup'const Routes = () => {return (<HashRouter><Switch><Route path="/" component={Home} exact /><Route path="/shop" component={Shop} /><Route path="/signin" component={Signin} /><Route path="/signup" component={Signup} /><PrivateRoute path="/user/dashboard" component={Dashboard} /><AdminRoute path="/admin/dashboard" component={AdminDashboard} /></Switch></HashRouter>)
}export default Routes
完善管理员 Dashboard 页面
// src\components\admin\AdminDashboard.tsx
import { Col, Descriptions, Menu, Row, Typography } from 'antd'
import { Link } from 'react-router-dom'
import Layout from '../core/Layout'
import { ShoppingCartOutlined, UserOutlined, OrderedListOutlined } from '@ant-design/icons'
import { Jwt } from '../../store/models/auth'
import { isAuth } from '../../helpers/auth'const { Title } = Typographyconst AdminDashboard = () => {const {user: { name, email }} = isAuth() as Jwtconst adminLinks = () => (<><Title level={5}>管理员链接</Title><Menu style={{ borderRight: 0 }}><Menu.Item><ShoppingCartOutlined style={{ marginRight: '5px' }} /><Link to="">添加分类</Link></Menu.Item><Menu.Item><UserOutlined style={{ marginRight: '5px' }} /><Link to="">添加产品</Link></Menu.Item><Menu.Item><OrderedListOutlined style={{ marginRight: '5px' }} /><Link to="">订单列表</Link></Menu.Item></Menu></>)const adminInfo = () => (<Descriptions title="管理员信息" bordered><Descriptions.Item label="昵称">{name}</Descriptions.Item><Descriptions.Item label="邮箱">{email}</Descriptions.Item><Descriptions.Item label="角色">管理员</Descriptions.Item></Descriptions>)return (<Layout title="管理员 dashboard" subTitle=""><Row><Col span="4">{adminLinks()}</Col><Col span="20">{adminInfo()}</Col></Row></Layout>)
}export default AdminDashboard
这篇关于React+Redux+Ant Design+TypeScript 电子商务实战-客户端应用 02 登录注册的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!