refactor(company): extract ExpireOrApp guard component from app.tsx

- Move ExpireOrApp logic into standalone component under components/ExpireOrApp
- Remove unused React hooks and CompanyExpireGuard import from app.tsx
- Use dayjs.utc for validity date to prevent timezone offset issues
- Remove stale console.log from CompanyExpireWarning
This commit is contained in:
2026-07-30 21:33:48 +08:00
parent 2a8d213c0f
commit 318728b121
4 changed files with 85 additions and 29 deletions

View File

@@ -3,6 +3,7 @@ import { PageLoading } from '@ant-design/pro-components';
import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max'; import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max';
import { history, Link, useAccess, useModel } from '@umijs/max'; import { history, Link, useAccess, useModel } from '@umijs/max';
import '@ant-design/v5-patch-for-react-19'; import '@ant-design/v5-patch-for-react-19';
import React from 'react';
import { App } from 'antd'; import { App } from 'antd';
import { AvatarDropdown, AvatarName, SelectLang } from '@/components'; import { AvatarDropdown, AvatarName, SelectLang } from '@/components';
import { currentUser as queryCurrentUser } from '@/services/api'; import { currentUser as queryCurrentUser } from '@/services/api';
@@ -17,7 +18,7 @@ import ServerErrorPage from './pages/500';
import { errorConfig } from './requestErrorConfig'; import { errorConfig } from './requestErrorConfig';
import { getDeviceId } from './utils/fp'; import { getDeviceId } from './utils/fp';
import './utils/sentry'; import './utils/sentry';
import CompanyExpireGuard from './components/CompanyExpireGuard'; import ExpireOrApp from './components/ExpireOrApp';
import CompanyExpireWarning from './components/CompanyExpireWarning'; import CompanyExpireWarning from './components/CompanyExpireWarning';
import GlobalErrorBoundary from './components/GlobalErrorBoundary'; import GlobalErrorBoundary from './components/GlobalErrorBoundary';
import PermissionGuard from './components/PermissionGuard'; import PermissionGuard from './components/PermissionGuard';
@@ -106,6 +107,10 @@ export function rootContainer(container: React.ReactNode) {
); );
} }
export function innerProvider(container: React.ReactNode) {
return <ExpireOrApp>{container}</ExpireOrApp>;
}
// ProLayout support API https://procomponents.ant.design/components/layout // ProLayout support API https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState }) => { export const layout: RunTimeLayoutConfig = ({ initialState }) => {
const { fetchUnread, initialized } = useModel('useMenuNoticeNum'); const { fetchUnread, initialized } = useModel('useMenuNoticeNum');
@@ -201,11 +206,6 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
childrenRender: (children) => { childrenRender: (children) => {
if (initialState?.loading) return <PageLoading />; if (initialState?.loading) return <PageLoading />;
const { company } = currentUser ?? {};
if (company?.special !== 0 && company?.validityStatus === 2) {
return <CompanyExpireGuard />;
}
return ( return (
<> <>
{currentUser && ( {currentUser && (

View File

@@ -19,11 +19,6 @@ const CompanyExpireWarning: React.FC = () => {
dayjs().utc(), dayjs().utc(),
'day', 'day',
); );
console.log(
expireInDays,
validityEndTime,
dayjs().utc().format('YYYY-MM-DD HH:mm:ss'),
);
const tipsNode = ( const tipsNode = (
<Alert <Alert

View File

@@ -0,0 +1,58 @@
import { PageLoading } from '@ant-design/pro-components';
import { history, useModel } from '@umijs/max';
import React, { useEffect, useRef, useState } from 'react';
import { LOGIN_URL } from '@/access';
import CompanyExpireGuard from '@/components/CompanyExpireGuard';
const ExpireOrApp: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { initialState, loading } = useModel('@@initialState');
const [location, setLocation] = useState(history.location);
const company = initialState?.currentUser?.company;
const guardRef = useRef<HTMLDivElement>(null);
useEffect(() => {
return history.listen((update) => {
setLocation(update.location);
});
}, []);
const isExpired = company?.special !== 0 && company?.validityStatus === 2;
const isLoginPage = location.pathname === LOGIN_URL;
const shouldBlock = isExpired && !isLoginPage;
const shouldBlockRef = useRef(shouldBlock);
shouldBlockRef.current = shouldBlock;
useEffect(() => {
if (!shouldBlock) return;
const el = guardRef.current;
if (!el) return;
const observer = new MutationObserver((mutations) => {
if (!shouldBlockRef.current) return;
for (const mutation of mutations) {
for (const node of Array.from(mutation.removedNodes)) {
if (node === el) {
window.location.reload();
break;
}
}
}
});
observer.observe(document.body, { childList: true });
return () => observer.disconnect();
}, [shouldBlock]);
if (loading) return <PageLoading />;
if (shouldBlock) {
return (
<div ref={guardRef} id="company-expire-guard-root">
<CompanyExpireGuard />
</div>
);
}
return <>{children}</>;
};
export default ExpireOrApp;

View File

@@ -50,7 +50,7 @@ export default (props: {
if (isSuperAdmin) { if (isSuperAdmin) {
values.validityEndTime = values.validityEndTime values.validityEndTime = values.validityEndTime
? dayjs(values.validityEndTime).endOf('day').format('YYYY-MM-DD') ? dayjs.utc(values.validityEndTime).format('YYYY-MM-DD')
: ''; : '';
values.validityStartTime = values.validityEndTime ? '2026-01-01' : ''; values.validityStartTime = values.validityEndTime ? '2026-01-01' : '';
} }
@@ -118,26 +118,29 @@ export default (props: {
}, },
]} ]}
/> />
{isSuperAdmin && (
<ProFormDatePicker
hidden={formValues?.id === void 0}
name="validityEndTime"
label={`${$t('form.company.validityEndTime')} (${$t('form.company.validityEndTime.tips')})`}
placeholder={$t('form.company.validityEndTime.placeholder')}
convertValue={(value: string | Dayjs | null | undefined) =>
value ? value : undefined
}
fieldProps={{
style: { width: '100%' },
disabledDate: (current: Dayjs) =>
current?.isBefore(dayjs().startOf('day')),
}}
// extra={<Text type="danger">{$t('pages.company.expired')}</Text>}
/>
)}
{formValues?.id !== void 0 && ( {formValues?.id !== void 0 && (
<> <>
{isSuperAdmin && (
<ProFormDatePicker
name="validityEndTime"
label={`${$t('form.company.validityEndTime')} (${$t('form.company.validityEndTime.tips')})`}
placeholder={$t('form.company.validityEndTime.placeholder')}
convertValue={(value: string | Dayjs | null | undefined) =>
value ? dayjs.utc(value) : undefined
}
fieldProps={{
style: { width: '100%' },
disabledDate: (current: Dayjs) => {
// return current?.isBefore(dayjs().startOf('day'));
return (
current?.format('YYYY-MM-DD') <
dayjs.utc().format('YYYY-MM-DD')
);
},
}}
/>
)}
<Divider size="small" /> <Divider size="small" />
<Text strong style={{ lineHeight: '40px', fontSize: 16 }}> <Text strong style={{ lineHeight: '40px', fontSize: 16 }}>