dark
7 months ago
6 changed files with 408 additions and 518 deletions
-
58src/App.tsx
-
87src/components/avatar/index.tsx
-
123src/pages/login/index.css
-
26src/pages/login/index.tsx
-
217src/request.ts
-
415src/routes.tsx
@ -1,51 +1,37 @@ |
|||
import { AppContextProvider } from '@/context.ts' |
|||
import { initI18n } from '@/i18n.ts' |
|||
import { appAtom, appStore, changeLanguage } from '@/store/system.ts' |
|||
import { userMenuDataAtom } from '@/store/user.ts' |
|||
import { IAppData } from '@/global' |
|||
import { ConfigProvider } from '@/components/config-provider' |
|||
import { Provider, useAtom, useAtomValue } from 'jotai' |
|||
import { Provider, useAtom } from 'jotai' |
|||
import './App.css' |
|||
import { useEffect } from 'react' |
|||
import { RootProvider } from './routes.tsx' |
|||
import PageLoading from '@/components/page-loading' |
|||
|
|||
|
|||
function App() { |
|||
|
|||
const [ appData, ] = useAtom(appAtom) |
|||
const { data = [], isLoading, isFetching, refetch } = useAtomValue(userMenuDataAtom) |
|||
|
|||
useEffect(() => { |
|||
initI18n() |
|||
}, []) |
|||
|
|||
|
|||
useEffect(() => { |
|||
if (appData.token) { |
|||
refetch().then() |
|||
} |
|||
}, [ appData.token ]) |
|||
|
|||
|
|||
if (isLoading || isFetching) { |
|||
return <PageLoading/> |
|||
} |
|||
|
|||
return ( |
|||
<ConfigProvider> |
|||
<AppContextProvider value={{ |
|||
get appData() { |
|||
return appData as IAppData |
|||
}, |
|||
changeLanguage |
|||
}}> |
|||
<Provider store={appStore}> |
|||
<RootProvider context={{ menuData: data }}/> |
|||
</Provider> |
|||
</AppContextProvider> |
|||
</ConfigProvider> |
|||
) |
|||
const [ appData, ] = useAtom(appAtom) |
|||
|
|||
useEffect(() => { |
|||
initI18n() |
|||
}, []) |
|||
|
|||
|
|||
return ( |
|||
<ConfigProvider> |
|||
<AppContextProvider value={{ |
|||
get appData() { |
|||
return appData as IAppData |
|||
}, |
|||
changeLanguage |
|||
}}> |
|||
<Provider store={appStore}> |
|||
<RootProvider context={{}}/> |
|||
</Provider> |
|||
</AppContextProvider> |
|||
</ConfigProvider> |
|||
) |
|||
} |
|||
|
|||
export default App |
@ -1,50 +1,63 @@ |
|||
import Icon from '@/components/icon' |
|||
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 { useAtomValue } from 'jotai' |
|||
import { useNavigate } from '@tanstack/react-router' |
|||
|
|||
const Avatar = () => { |
|||
|
|||
const { t } = useTranslation() |
|||
const { data, isLoading } = useAtomValue(currentUserAtom) |
|||
const { t } = useTranslation() |
|||
const { data, isLoading } = useAtomValue(currentUserAtom) |
|||
const { mutate: logout } = useAtomValue(logoutAtom) |
|||
const navigate = useNavigate() |
|||
|
|||
return ( |
|||
<div> |
|||
<Dropdown |
|||
key={'user'} |
|||
placement="bottomRight" |
|||
menu={{ |
|||
items: [ |
|||
{ |
|||
key: 'logout', |
|||
icon: <Icon type={'park:Logout'}/>, |
|||
label: <span style={{ |
|||
marginInlineStart: 8, |
|||
userSelect: 'none' |
|||
}}>{t('app.header.logout')}</span>, |
|||
}, |
|||
], |
|||
}} |
|||
> |
|||
<Spin spinning={isLoading}> |
|||
<AntAvatar |
|||
key="avatar" |
|||
size={'small'} |
|||
src={data?.avatar || data?.nickname?.substring(0, 1)}> |
|||
{!data?.avatar && data?.nickname?.substring(0, 1)} |
|||
</AntAvatar> |
|||
<span key="name" |
|||
style={{ |
|||
marginInlineStart: 8, |
|||
userSelect: 'none' |
|||
}}> |
|||
return ( |
|||
<div> |
|||
<Dropdown |
|||
key={'user'} |
|||
placement="bottomRight" |
|||
menu={{ |
|||
items: [ |
|||
{ |
|||
key: 'logout', |
|||
icon: <Icon type={'park:Logout'}/>, |
|||
label: <span style={{ |
|||
marginInlineStart: 8, |
|||
userSelect: 'none' |
|||
}}>{t('app.header.logout')}</span>, |
|||
}, |
|||
], |
|||
onClick: (e) => { |
|||
if (e.key === 'logout') { |
|||
logout() |
|||
navigate({ |
|||
to: '/login', search: { |
|||
redirect: window.location.pathname |
|||
} |
|||
}) |
|||
} |
|||
}, |
|||
}} |
|||
> |
|||
<Spin spinning={isLoading}> |
|||
<AntAvatar |
|||
key="avatar" |
|||
size={'small'} |
|||
src={data?.avatar || data?.nickname?.substring(0, 1)}> |
|||
{!data?.avatar && data?.nickname?.substring(0, 1)} |
|||
</AntAvatar> |
|||
<span key="name" |
|||
style={{ |
|||
marginInlineStart: 8, |
|||
userSelect: 'none' |
|||
}}> |
|||
{data?.nickname} |
|||
</span> |
|||
</Spin> |
|||
</Dropdown> |
|||
</div> |
|||
) |
|||
</Spin> |
|||
</Dropdown> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default Avatar |
@ -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%; |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue