新项目, antd6, react19

This commit is contained in:
zhengw
2026-01-05 17:06:16 +08:00
commit fcb43c0cbd
84 changed files with 6939 additions and 0 deletions

129
src/layouts/AppLayout.tsx Normal file
View File

@@ -0,0 +1,129 @@
import { Layout, Spin } from 'antd';
import { Content, Header } from 'antd/es/layout/layout';
import Sider from 'antd/es/layout/Sider';
import { useEffect, useState } from 'react';
import Footer from '@/components/Footer';
import { GapBox } from '@/components/GapBox';
import { HeaderUserInfo } from '@/components/Header/HeaderUserInfo';
import { NavigateMenuDrawer } from '@/components/SiderMenu/NavigateMenuDrawer';
import NavMenu from '@/components/SiderMenu/NavMenu';
import TabNavPlugin from '@/components/TabNavPlugin';
import { DefaultERPName, headerHeight } from '@/configs/config';
import Outlet from '@/router/Outlet';
import { useAuthStore } from '@/store/AuthStore';
import { useUserStore } from '@/store/UserStore';
import { getDevice, toObject } from '@/utils/common';
import { loginState } from './base';
import ErrorBoundary from './ErrorBoundary';
const AppLayout = () => {
const [loading, setLoading] = useState(true);
const isPhone = getDevice() == 'phone';
const userStore = useUserStore();
const authStore = useAuthStore();
useEffect(() => {
setLoading(true);
loginState().then((res) => {
// console.log(res);
userStore.updateUser(toObject(res.user_info));
authStore.updateAuth(toObject(res.auth_info) as any);
localStorage.setItem('admin_user_id', res?.user_info?.user_id);
setLoading(false);
});
}, []);
return (
<>
{!loading ? (
<Layout style={{ minHeight: '100vh' }}>
<Header
style={{
background: '#fff',
position: 'sticky',
top: 0,
zIndex: 101,
width: '100%',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
paddingLeft: 12,
paddingRight: 12,
borderBottom: '1px solid #ddd',
height: headerHeight,
flexShrink: 0,
lineHeight: 1,
boxSizing: 'border-box',
}}
>
<GapBox style={{ display: 'inline-flex' }}>
{isPhone ? (
<NavigateMenuDrawer />
) : (
<div style={{ fontWeight: 'bold', fontSize: 16 }}>{DefaultERPName}</div>
)}
{Date.now()}
</GapBox>
<div style={{ display: 'inline-flex' }}>
<HeaderUserInfo />
</div>
</Header>
<Layout style={{ justifyContent: 'flex-start' }}>
{isPhone ? null : (
<Sider
// collapsible
// onCollapse={(collapsed: boolean) => {
// setCollapsed(collapsed);
// }}
style={{
background: '#fff',
overflow: 'auto',
// height: `calc(100vh - ${headerHeight}px)`,
position: 'sticky',
// zIndex: 1000,
left: 0,
// top: headerHeight,
}}
width={200}
// width={window?.dfConfig?.language == 'zh-cn' ? 100 : 240}
// collapsed={collapsed}
// collapsedWidth={60}
>
<NavMenu />
{/* <NavMenuCard left={100} /> */}
</Sider>
)}
<Content
style={{
// minHeight: `calc(100vh - ${headerHeight}px)`,
padding: isPhone ? '12px 0 0 0' : '12px 12px 0 12px',
overflow: 'inherit',
background: '#F4F4F4',
boxSizing: 'border-box',
minWidth: 0,
}}
>
<TabNavPlugin />
{/* <SelfAccount /> */}
<div style={{ minHeight: `calc(100vh - ${headerHeight}px - 32px - 50px)`, background: '#fff' }}>
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
</div>
<Footer />
</Content>
</Layout>
</Layout>
) : (
<Spin
style={{ height: '90vh', display: 'flex', alignItems: 'center', justifyContent: 'center', maxHeight: '90vh' }}
size='large'
tip={<span style={{ color: '#000' }}>...</span>}
>
&nbsp;
</Spin>
)}
</>
);
};
export default AppLayout;

View File

@@ -0,0 +1,11 @@
import Outlet from '@/router/Outlet';
import ErrorBoundary from './ErrorBoundary';
const EmptyLayout = () => {
return (
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
);
};
export default EmptyLayout;

View File

@@ -0,0 +1,81 @@
import { Button, Result } from 'antd';
import React from 'react';
type IProps = {
fallback?: (error: any, info?: any) => React.ReactNode;
children?: React.ReactNode;
};
/** 错误边界组件 */
class ErrorBoundary extends React.Component<IProps> {
state: { hasError: boolean; errorMsg?: React.ReactNode };
constructor(props: IProps) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(_error: any) {
// console.log("error", error);
// 更新状态,以便下一次渲染将显示后备 UI。
return { hasError: true };
}
componentDidCatch(error: any, info: any) {
// console.log(error, info);
this.setState({ errorMsg: `${error}${info.componentStack}` });
}
render() {
if (this.state.hasError) {
// 你可以渲染任何自定义后备 UI
return (
this.props?.fallback?.(this.state.errorMsg) || (
<Result
status='error'
title={
<div style={{ fontSize: 20 }}>
<div></div>
<div></div>
</div>
}
subTitle={
<pre
style={{
color: '#ff4d4f',
whiteSpace: 'break-spaces',
padding: 20,
textAlign: 'left',
margin: 0,
}}
>
{this.state.errorMsg}
</pre>
}
style={{
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
minHeight: 'calc(100vh - 180px)',
}}
extra={[
<Button
type='primary'
key='console'
onClick={() => {
window.location.reload();
}}
>
</Button>,
]}
/>
)
);
}
return this.props?.children;
}
}
export default ErrorBoundary;

13
src/layouts/base.ts Normal file
View File

@@ -0,0 +1,13 @@
import { requestLite } from '@/utils/useRequest';
export const loginState = async () => {
return new Promise<any>((resolve, reject) => {
requestLite('/Users/loginState').then((res) => {
if (res?.err_code == 0) {
resolve(res);
} else {
reject(null);
}
});
});
};