You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
GHHub/src/views/App.jsx

191 lines
5.8 KiB
React

import { Outlet, Link, useHref, useNavigate, useLocation, NavLink } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { Layout, Menu, ConfigProvider, theme, Dropdown, Space, Row, Col, Badge, Typography, Modal, Input, Button, App as AntApp } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import 'antd/dist/reset.css';
import AppLogo from '@/assets/logo-gh.png';
import { isEmpty } from '@/utils/commons';
import { appendRequestParams } from '@/utils/request'
import Language from '../i18n/LanguageSwitcher';
import { useTranslation } from 'react-i18next';
import zhLocale from 'antd/locale/zh_CN';
import enLocale from 'antd/locale/en_US';
import 'dayjs/locale/zh-cn';
import { BUILD_VERSION, } from '@/config';
import useNoticeStore from '@/stores/Notice';
import useAuthStore from '@/stores/Auth'
import { useStorage } from '@/hooks/useStorage'
const { Header, Content, Footer } = Layout;
const { Title } = Typography;
function App() {
const { t, i18n } = useTranslation();
const [password, setPassword] = useState('');
const { notification } = AntApp.useApp();
const [loginUser, validateUserPassword] = useAuthStore((state) => [state.loginUser, state.validateUserPassword])
const { loginToken, username } = useStorage()
const noticeUnRead = useNoticeStore((state) => state.noticeUnRead)
const href = useHref();
const navigate = useNavigate()
const location = useLocation()
// 除了路由 /p...以外都需要登陆系统
const needToLogin = href !== '/login' && isEmpty(loginToken)
if (!needToLogin) {
appendRequestParams('token', loginToken)
}
useEffect(() => {
if (needToLogin) {
navigate('/login')
}
}, [href])
useEffect(() => {
window.gtag('event', 'page_view', { page_location: window.location.href });
}, [location]);
const onSubmit = () => {
validateUserPassword(username, password)
.catch(ex => {
notification.error({
message: `Notification`,
description: ex.message,
placement: 'top',
duration: 4,
});
});
setPassword('');
};
const splitPath = href.split('/');
let defaultPath = 'reservation';
if (splitPath.length > 1) {
defaultPath = splitPath[1];
}
const {
token: { colorBgContainer },
} = theme.useToken();
const [antdLng, setAntdLng] = useState(enLocale);
useEffect(() => {
setAntdLng(i18n.language === 'en' ? enLocale : zhLocale);
}, [i18n.language]);
return (
<ConfigProvider locale={antdLng}
theme={{
token: {
colorPrimary: '#00b96b',
},
algorithm: theme.defaultAlgorithm,
}}>
<AntApp>
<Modal
centered
closable={false}
maskClosable={false}
footer={null}
open={loginUser.timeout}
>
<Title level={3}>{t('LoginTimeout')}</Title>
<div>{t('LoginTimeoutTip')}</div>
<Space direction='horizontal'>
<Input.Password value={password}
onChange={(e) => setPassword(e.target.value)}
onPressEnter={() => onSubmit()}
addonBefore={loginUser.username} />
<Button
onClick={() => onSubmit()}
>{t('Submit')}</Button></Space>
</Modal>
<Layout
style={{
minHeight: '100vh',
}}>
<Header className='header' style={{ position: 'sticky', top: 0, zIndex: 1, width: '100%' }}>
<Row gutter={{ md: 24 }} justify='end' align='middle'>
<Col span={16}>
<NavLink to='/'>
<img src={AppLogo} className='logo' alt='App logo' />
</NavLink>
<Menu
theme='dark'
mode='horizontal'
selectedKeys={[defaultPath]}
items={[
{ key: 'reservation', label: <Link to='/reservation/newest'>{t('menu.Reservation')}</Link> },
{ key: 'invoice', label: <Link to='/invoice'>{t('menu.Invoice')}</Link> },
{ key: 'feedback', label: <Link to='/feedback'>{t('menu.Feedback')}</Link> },
{ key: 'report', label: <Link to='/report'>{t('menu.Report')}</Link> },
2 years ago
{
key: 'notice',
2 years ago
label: (
<Link to='/notice'>
{t('menu.Notice')}
{noticeUnRead ? <Badge dot /> : ''}
2 years ago
</Link>
),
},
]}
/>
</Col>
<Col span={4}>
<Title level={3} style={{ color: 'white', marginBottom: '0', display: 'flex', justifyContent: 'end' }}>
{loginUser.travelAgencyName}
2 years ago
</Title>
</Col>
<Col span={2}>
<Dropdown
menu={{
items: [...[
{ label: <Link to='/account/change-password'>{t('ChangePassword')}</Link>, key: '0' },
{ label: <Link to='/account/profile'>{t('Profile')}</Link>, key: '1' },
{ type: 'divider' },
{ label: <Link to='/logout'>{t('Logout')}</Link>, key: '3' },
],
{ type: 'divider' },
{ label: <>v{BUILD_VERSION}</>, key: 'BUILD_VERSION' },
],
}}
trigger={['click']}
>
<a onClick={e => e.preventDefault()}>
<Space>
{loginUser.username}
<DownOutlined />
</Space>
</a>
</Dropdown>
</Col>
<Col span={2}>
<Language />
</Col>
</Row>
</Header>
<Content
style={{
padding: 24,
margin: 0,
minHeight: 280,
background: colorBgContainer,
}}>
{needToLogin ? <>login...</> : <Outlet />}
</Content>
<Footer></Footer>
</Layout>
</AntApp>
</ConfigProvider>
);
}
export default App