Browse Source

优化登录,退出功能

main
dark 7 months ago
parent
commit
f178bbf5dc
  1. 18
      src/App.tsx
  2. 15
      src/components/avatar/index.tsx
  3. 123
      src/pages/login/index.css
  4. 22
      src/pages/login/index.tsx
  5. 7
      src/request.ts
  6. 27
      src/routes.tsx

18
src/App.tsx

@ -1,37 +1,23 @@
import { AppContextProvider } from '@/context.ts' import { AppContextProvider } from '@/context.ts'
import { initI18n } from '@/i18n.ts' import { initI18n } from '@/i18n.ts'
import { appAtom, appStore, changeLanguage } from '@/store/system.ts' import { appAtom, appStore, changeLanguage } from '@/store/system.ts'
import { userMenuDataAtom } from '@/store/user.ts'
import { IAppData } from '@/global' import { IAppData } from '@/global'
import { ConfigProvider } from '@/components/config-provider' import { ConfigProvider } from '@/components/config-provider'
import { Provider, useAtom, useAtomValue } from 'jotai'
import { Provider, useAtom } from 'jotai'
import './App.css' import './App.css'
import { useEffect } from 'react' import { useEffect } from 'react'
import { RootProvider } from './routes.tsx' import { RootProvider } from './routes.tsx'
import PageLoading from '@/components/page-loading'
function App() { function App() {
const [ appData, ] = useAtom(appAtom) const [ appData, ] = useAtom(appAtom)
const { data = [], isLoading, isFetching, refetch } = useAtomValue(userMenuDataAtom)
useEffect(() => { useEffect(() => {
initI18n() initI18n()
}, []) }, [])
useEffect(() => {
if (appData.token) {
refetch().then()
}
}, [ appData.token ])
if (isLoading || isFetching) {
return <PageLoading/>
}
return ( return (
<ConfigProvider> <ConfigProvider>
<AppContextProvider value={{ <AppContextProvider value={{
@ -41,7 +27,7 @@ function App() {
changeLanguage changeLanguage
}}> }}>
<Provider store={appStore}> <Provider store={appStore}>
<RootProvider context={{ menuData: data }}/>
<RootProvider context={{}}/>
</Provider> </Provider>
</AppContextProvider> </AppContextProvider>
</ConfigProvider> </ConfigProvider>

15
src/components/avatar/index.tsx

@ -1,13 +1,16 @@
import Icon from '@/components/icon' import Icon from '@/components/icon'
import { useTranslation } from '@/i18n.ts' import { useTranslation } from '@/i18n.ts'
import { currentUserAtom } from '@/store/user.ts'
import { currentUserAtom, logoutAtom } from '@/store/user.ts'
import { Avatar as AntAvatar, Dropdown, Spin } from 'antd' import { Avatar as AntAvatar, Dropdown, Spin } from 'antd'
import { useAtomValue } from 'jotai' import { useAtomValue } from 'jotai'
import { useNavigate } from '@tanstack/react-router'
const Avatar = () => { const Avatar = () => {
const { t } = useTranslation() const { t } = useTranslation()
const { data, isLoading } = useAtomValue(currentUserAtom) const { data, isLoading } = useAtomValue(currentUserAtom)
const { mutate: logout } = useAtomValue(logoutAtom)
const navigate = useNavigate()
return ( return (
<div> <div>
@ -25,6 +28,16 @@ const Avatar = () => {
}}>{t('app.header.logout')}</span>, }}>{t('app.header.logout')}</span>,
}, },
], ],
onClick: (e) => {
if (e.key === 'logout') {
logout()
navigate({
to: '/login', search: {
redirect: window.location.pathname
}
})
}
},
}} }}
> >
<Spin spinning={isLoading}> <Spin spinning={isLoading}>

123
src/pages/login/index.css

@ -1,123 +0,0 @@
body {
margin: 0;
padding: 0;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
overflow: hidden;
.login-container {
display: flex;
align-items: center;
height: 100vh;
background-image: url("@/assets/login.png");
background-repeat: no-repeat;
background-size: cover;
.language {
position: absolute;
top: 10px;
right: 10px;
color: #fff;
font-size: 14px;
cursor: pointer;
}
.loginBlock {
width: 100%;
height: 100%;
padding: 40px 0;
display: flex;
align-items: center;
justify-content: center;
}
.innerBlock {
width: 356px;
margin: 0 auto;
}
.logo {
height: 30px;
}
.infoLine {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0;
}
.infoLeft {
color: #666;
font-size: 14px;
}
.desc {
margin: 24px 0;
color: #999;
font-size: 16px;
cursor: pointer;
}
.active {
color: #333;
font-weight: bold;
font-size: 24px;
}
.line {
display: inline-block;
width: 1px;
height: 12px;
background: #999;
}
.innerBeforeInput {
margin-left: 10px;
color: #999;
}
.innerBeforeInput .line {
margin-left: 10px;
}
.innerAfterInput {
margin-right: 10px;
color: #999;
}
.innerAfterInput .line {
margin-right: 10px;
vertical-align: middle;
}
.sendCode {
max-width: 65px;
margin-right: 10px;
}
.otherLogin {
color: #666;
font-size: 14px;
}
.icon {
margin-left: 10px;
}
.link {
color: #5584ff;
font-size: 14px;
text-align: left;
}
.submitBtn {
width: 100%;
}
}
}

22
src/pages/login/index.tsx

@ -1,22 +1,18 @@
import SelectLang from '@/components/select-lang' import SelectLang from '@/components/select-lang'
import { createFileRoute, useSearch, useNavigate } from '@tanstack/react-router'
import { Button, Form, Input, message, Space } from 'antd'
import { useAtom } from 'jotai'
import { createFileRoute } from '@tanstack/react-router'
import { Button, Form, Input, Space } from 'antd'
import { useAtom, useAtomValue } from 'jotai'
import { useTranslation } from '@/i18n.ts' import { useTranslation } from '@/i18n.ts'
import { loginAtom, loginFormAtom } from '@/store/user.ts' import { loginAtom, loginFormAtom } from '@/store/user.ts'
import { memo, useEffect, useLayoutEffect } from 'react'
// import './index.css'
import { memo, useLayoutEffect } from 'react'
import { useStyles } from './style.ts' import { useStyles } from './style.ts'
const Login = memo(() => { const Login = memo(() => {
const { styles } = useStyles() const { styles } = useStyles()
const navigator = useNavigate()
// @ts-ignore 从url中获取redirect参数
const search = useSearch({ form: '/login' })
const { t } = useTranslation() const { t } = useTranslation()
const [ values, setValues ] = useAtom(loginFormAtom) const [ values, setValues ] = useAtom(loginFormAtom)
const [ { isPending, isSuccess, mutate } ] = useAtom(loginAtom)
const { isPending, mutate } = useAtomValue(loginAtom)
const [ form ] = Form.useForm() const [ form ] = Form.useForm()
const handleSubmit = () => { const handleSubmit = () => {
@ -25,14 +21,6 @@ const Login = memo(() => {
}) })
} }
useEffect(() => {
if (isSuccess) {
message.success(t('login.success'))
navigator({
to: search?.redirect ?? '/'
})
}
}, [ isSuccess ])
useLayoutEffect(() => { useLayoutEffect(() => {

7
src/request.ts

@ -52,6 +52,12 @@ axiosInstance.interceptors.response.use(
//login //login
if (response.config.url?.includes('/sys/login')) { if (response.config.url?.includes('/sys/login')) {
setToken(result.data.token) setToken(result.data.token)
const search = new URLSearchParams(window.location.search)
// eslint-disable-next-line no-case-declarations
const redirect = search.get('redirect')
if (redirect) {
window.location.href = redirect
}
} }
return response return response
case 401: case 401:
@ -60,7 +66,6 @@ axiosInstance.interceptors.response.use(
return Promise.reject(new Error('to login')) return Promise.reject(new Error('to login'))
} }
// 401: 未登录 // 401: 未登录
message.error('登录失败,跳转重新登录') message.error('登录失败,跳转重新登录')
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations

27
src/routes.tsx

@ -22,10 +22,12 @@ import {
RouterProvider, RouterProvider,
} from '@tanstack/react-router' } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools' import { TanStackRouterDevtools } from '@tanstack/router-devtools'
import { memo } from 'react'
import { memo, useEffect, useRef } from 'react'
import RootLayout from './layout/RootLayout' import RootLayout from './layout/RootLayout'
import { IRootContext, MenuItem } from './global' import { IRootContext, MenuItem } from './global'
import { DevTools } from 'jotai-devtools' import { DevTools } from 'jotai-devtools'
import { useAtomValue } from 'jotai'
import { userMenuDataAtom } from '@/store/user.ts'
const PageRootLayout = () => { const PageRootLayout = () => {
return <PageStoreProvider> return <PageStoreProvider>
@ -279,7 +281,25 @@ const routeTree = rootRoute.addChildren(
export const RootProvider = memo((props: { context: Partial<IRootContext> }) => { export const RootProvider = memo((props: { context: Partial<IRootContext> }) => {
generateDynamicRoutes(props.context.menuData ?? [], layoutAuthRoute)
const { data: menuData, isLoading, refetch } = useAtomValue(userMenuDataAtom)
const isFetchRef = useRef(false)
useEffect(() => {
if (isFetchRef.current) {
return
}
isFetchRef.current = true
refetch()
}, [])
if (isLoading) {
return <PageLoading/>
}
generateDynamicRoutes(menuData ?? [], layoutAuthRoute)
const router = createRouter({ const router = createRouter({
routeTree, routeTree,
@ -288,9 +308,10 @@ export const RootProvider = memo((props: { context: Partial<IRootContext> }) =>
defaultPendingComponent: () => <Loading loading={true} delay={300}/> defaultPendingComponent: () => <Loading loading={true} delay={300}/>
}) })
return ( return (
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<RouterProvider router={router} context={{ ...props.context, queryClient }}/>
<RouterProvider router={router} context={{ ...props.context, menuData, queryClient }}/>
</QueryClientProvider> </QueryClientProvider>
) )
}) })
Loading…
Cancel
Save