Merge branch 'dev' of gitea.51mars.com:Oak-Team/oak-frontend-base into dev
This commit is contained in:
commit
6283c03f63
|
|
@ -7,5 +7,6 @@ declare const _default: <ED2 extends ED, T2 extends keyof ED2>(props: ReactCompo
|
|||
desc?: string | undefined;
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
content?: React.ReactNode;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,21 @@ const DefaultErrorInfo = {
|
|||
desc: '抱歉,您正在使用的浏览器版本过低,无法打开当前网页。',
|
||||
imagePath: './assets/svg/assets-result-browser-incompatible.svg',
|
||||
},
|
||||
[ECode.pageCrash]: {
|
||||
title: '抱歉,网页崩溃了',
|
||||
desc: '网页似乎遇到了问题,我们将尽快解决,请稍后重新加载。',
|
||||
imagePath: './assets/svg/assets-result-404.svg',
|
||||
},
|
||||
[ECode.pageUpdate]: {
|
||||
title: '网页有更新',
|
||||
desc: '检查到网页有更新,请刷新以查看最新内容。',
|
||||
imagePath: './assets/svg/assets-result-404.svg',
|
||||
},
|
||||
[ECode.pageDataCacheFailure]: {
|
||||
title: '数据缓存失效',
|
||||
desc: '非常抱歉,数据缓存已失效。请点击【清除缓存】以更新信息。',
|
||||
imagePath: './assets/svg/assets-result-404.svg',
|
||||
},
|
||||
};
|
||||
export default OakComponent({
|
||||
isList: false,
|
||||
|
|
@ -65,5 +80,8 @@ export default OakComponent({
|
|||
goBack(delta) {
|
||||
this.navigateBack(delta);
|
||||
},
|
||||
async clearData() {
|
||||
await this.features.localStorage.clear();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ interface IErrorPageProps {
|
|||
desc?: string;
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
content?: React.ReactNode;
|
||||
}
|
||||
export default function Render(props: WebComponentProps<ED, keyof ED, false, IErrorPageProps, {
|
||||
goBack: (delta?: number) => void;
|
||||
clearData: () => Promise<void>;
|
||||
}>): React.JSX.Element;
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -39,12 +39,28 @@ const errorInfo = {
|
|||
desc: '系统维护中,请稍后再试。',
|
||||
icon: <LightMaintenanceIcon />,
|
||||
},
|
||||
[ECode.pageCrash]: {
|
||||
title: '抱歉,网页崩溃了',
|
||||
desc: '网页似乎遇到了问题,我们将尽快解决,请稍后重新加载。',
|
||||
icon: <Light404Icon />,
|
||||
},
|
||||
[ECode.pageUpdate]: {
|
||||
title: '网页有更新',
|
||||
desc: '检查到网页有更新,请刷新以查看最新内容。',
|
||||
icon: <Light404Icon />,
|
||||
},
|
||||
[ECode.pageDataCacheFailure]: {
|
||||
title: '数据缓存失效',
|
||||
desc: '非常抱歉,数据缓存已失效。请点击【清除缓存】以更新信息。',
|
||||
icon: <Light404Icon />,
|
||||
},
|
||||
};
|
||||
export default function Render(props) {
|
||||
const { code, icon, title, desc, children } = props.data;
|
||||
const { t, goBack } = props.methods;
|
||||
const { code, icon, title, desc, children, content } = props.data;
|
||||
const { t, goBack, clearData } = props.methods;
|
||||
const info = errorInfo[code];
|
||||
const prefixCls = 'oak';
|
||||
const backed = code === ECode.notFound || code === ECode.forbidden;
|
||||
return (<div className={`${prefixCls}-errorPage`}>
|
||||
{icon || info?.icon}
|
||||
<div className={`${prefixCls}-errorPage__title`}>
|
||||
|
|
@ -53,10 +69,37 @@ export default function Render(props) {
|
|||
<div className={`${prefixCls}-errorPage__description`}>
|
||||
{desc || info?.desc}
|
||||
</div>
|
||||
{content}
|
||||
{children || (<Button type="primary" onClick={() => {
|
||||
goBack();
|
||||
switch (code) {
|
||||
case ECode.forbidden:
|
||||
case ECode.notFound: {
|
||||
goBack();
|
||||
break;
|
||||
}
|
||||
case ECode.pageDataCacheFailure: {
|
||||
clearData();
|
||||
window.location.reload();
|
||||
break;
|
||||
}
|
||||
case ECode.networkError:
|
||||
case ECode.browserIncompatible:
|
||||
case ECode.maintenance:
|
||||
case ECode.pageCrash:
|
||||
case ECode.pageUpdate:
|
||||
default: {
|
||||
window.location.reload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}}>
|
||||
返回
|
||||
{backed
|
||||
? t('common::back', {
|
||||
'#oakModule': 'oak-frontend-base',
|
||||
})
|
||||
: t('common::refresh', {
|
||||
'#oakModule': 'oak-frontend-base',
|
||||
})}
|
||||
</Button>)}
|
||||
</div>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,8 +66,6 @@ export default function Render(props) {
|
|||
case 'DatePicker': {
|
||||
const { dateProps } = column;
|
||||
const { showTime = false } = dateProps || {};
|
||||
//assert(op, '选择时间,算子必须传入');
|
||||
const unitOfTime = 'day';
|
||||
V = (<DatePicker placeholder={placeholder || t('placeholder.select')} style={{ width: '100%' }} format="YYYY-MM-DD" showTime={showTime} onChange={(date, dateString) => {
|
||||
setFilterAndResetFilter(viewType, date);
|
||||
}}/>);
|
||||
|
|
@ -98,6 +96,3 @@ export default function Render(props) {
|
|||
<>{V}</>
|
||||
</Form.Item>);
|
||||
}
|
||||
function assertMessage(attr, attrType, op, ops) {
|
||||
return `attr为【${attr}】, 传入的算子【${op}】不支持,类型【${attrType}】只支持【${JSON.stringify(ops)}】`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,16 +21,17 @@ export default function render(props) {
|
|||
}} options={data.map((ele) => ({
|
||||
value: ele.id,
|
||||
label: ele.title,
|
||||
key: ele.id,
|
||||
}))} multiple={multiple}></Selector>);
|
||||
}
|
||||
case 'radio': {
|
||||
if (multiple) {
|
||||
return (<Checkbox.Group value={entityIds} onChange={(value) => onChange(value)}>
|
||||
{data.map((ele) => (<Checkbox value={ele.id}>{ele.title}</Checkbox>))}
|
||||
{data.map((ele) => (<Checkbox key={ele.id} value={ele.id}>{ele.title}</Checkbox>))}
|
||||
</Checkbox.Group>);
|
||||
}
|
||||
return (<Radio.Group onChange={(value) => onChange([value])} value={entityId}>
|
||||
{data.map((ele) => (<Radio value={ele.id}>{ele.title}</Radio>))}
|
||||
{data.map((ele) => (<Radio key={ele.id} value={ele.id}>{ele.title}</Radio>))}
|
||||
</Radio.Group>);
|
||||
}
|
||||
case 'list': {
|
||||
|
|
|
|||
|
|
@ -38,11 +38,13 @@ export default function render(props) {
|
|||
return (<Checkbox.Group options={data.map((ele) => ({
|
||||
value: ele.id,
|
||||
label: ele.title,
|
||||
key: ele.id,
|
||||
}))} value={entityIds} onChange={(value) => onChange(value)}/>);
|
||||
}
|
||||
return (<Radio.Group onChange={({ target }) => onChange(target.value)} value={entityId} options={data.map((ele) => ({
|
||||
value: ele.id,
|
||||
label: ele.title,
|
||||
key: ele.id,
|
||||
}))}></Radio.Group>);
|
||||
}
|
||||
case 'list': {
|
||||
|
|
|
|||
|
|
@ -765,7 +765,10 @@ class ListNode extends EntityNode {
|
|||
const { data, sorter, filter } = this.constructSelection(true, false, true);
|
||||
const ids2 = ids.concat(createIds);
|
||||
/**
|
||||
* 在非modi状态下,所取数据是在ids2中满足filter的部分(自身对象和其它对象的更新可能会影响这些行不再满足条件)
|
||||
* 在非modi状态下,当前的逻辑是:
|
||||
* 若本结点非脏(其子结点也非脏),直接用ids作为查询条件(这里也隐喻着父结点不会影响这里的数据)
|
||||
* 若本结点为脏,则要考虑当前这些ids是否还满足filters,若一个结点出现这种情况要考虑,则要自己保证filter中的关联关系数据都在cache当中,不然会出现数据为空
|
||||
* by Xc 20250308
|
||||
*/
|
||||
const filter2 = inModiNextBranch ? filter : (ids2.length > 0 ? (filter && this.isDirty() ? combineFilters(this.entity, this.cache.getSchema(), [filter, {
|
||||
id: {
|
||||
|
|
|
|||
|
|
@ -18,5 +18,21 @@
|
|||
"unknown": {
|
||||
"title": "未知异常",
|
||||
"content": "抱歉,出现未知异常,可能是数据缓存失效,需要清除缓存,请点击【清除缓存】再尝试"
|
||||
},
|
||||
"timeout": {
|
||||
"title": "请求超时",
|
||||
"content": "非常抱歉,请求超时。请稍后再试"
|
||||
},
|
||||
"clockDrift": {
|
||||
"title": "网络请求错误",
|
||||
"content": "非常抱歉,网络请求出现了问题。请稍后再试,我们正在努力修复"
|
||||
},
|
||||
"pageCrash": {
|
||||
"title": "抱歉,网页崩溃了",
|
||||
"content": "网页似乎遇到了问题,我们将尽快解决,请稍后重新加载"
|
||||
},
|
||||
"pageUpdate": {
|
||||
"title": "网页有更新",
|
||||
"content": "检查到网页有更新,请刷新以查看最新内容"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,18 @@
|
|||
import React, { lazy } from 'react';
|
||||
const Message = lazy(() => import('../../../components/message'));
|
||||
const DebugPanel = lazy(() => import('../../../components/func/debugPanel'));
|
||||
const ErrorBoundary = lazy(() => import('./ErrorBoundary'));
|
||||
const AppContainer = (props) => {
|
||||
const { children } = props;
|
||||
return (<React.Fragment>
|
||||
<React.Suspense fallback={<></>}>
|
||||
<Message />
|
||||
</React.Suspense>
|
||||
{children}
|
||||
<React.Suspense fallback={<></>}>
|
||||
<ErrorBoundary>
|
||||
{children}
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
<React.Suspense fallback={<></>}>
|
||||
{process.env.NODE_ENV === 'development' && <DebugPanel />}
|
||||
</React.Suspense>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { lazy } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import { OakException, OakNetworkException, OakServerProxyException, } from 'oak-domain/lib/types/Exception';
|
||||
import { OakException, OakNetworkException, OakServerProxyException, OakClockDriftException, OakRequestTimeoutException, } from 'oak-domain/lib/types/Exception';
|
||||
import ErrorMessage from './ErrorMessage';
|
||||
import { ECode } from '../../../types/ErrorPage';
|
||||
const ErrorPage = lazy(() => import('../../../components/errorPage'));
|
||||
function Error(props) {
|
||||
|
|
@ -13,8 +14,7 @@ function Error(props) {
|
|||
'#oakModule': "oak-frontend-base"
|
||||
})} desc={locales.t('error::network.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}>
|
||||
<ErrorMessage error={error}/>
|
||||
})} content={<ErrorMessage error={error}/>}>
|
||||
<Button type="primary" onClick={async () => {
|
||||
window.location.reload();
|
||||
}}>
|
||||
|
|
@ -30,8 +30,41 @@ function Error(props) {
|
|||
'#oakModule': "oak-frontend-base"
|
||||
})} desc={locales.t('error::proxy.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}>
|
||||
<ErrorMessage error={error}/>
|
||||
})} content={<ErrorMessage error={error}/>}>
|
||||
<Button type="primary" onClick={async () => {
|
||||
window.location.reload();
|
||||
}}>
|
||||
{locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
</Button>
|
||||
</ErrorPage>);
|
||||
}
|
||||
if (error instanceof OakClockDriftException) {
|
||||
// 服务器加密报错
|
||||
return (<React.Suspense fallback={null}>
|
||||
<ErrorPage code={ECode.error} title={locales.t('error::clockDrift.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})} desc={locales.t('error::clockDrift.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})} content={<ErrorMessage error={error}/>}>
|
||||
<Button type="primary" onClick={async () => {
|
||||
window.location.reload();
|
||||
}}>
|
||||
{locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
</Button>
|
||||
</ErrorPage>
|
||||
</React.Suspense>);
|
||||
}
|
||||
if (error instanceof OakRequestTimeoutException) {
|
||||
// 服务器超时报错
|
||||
return (<ErrorPage code={ECode.error} title={locales.t('error::timeout.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})} desc={locales.t('error::timeout.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})} content={<ErrorMessage error={error}/>}>
|
||||
<Button type="primary" onClick={async () => {
|
||||
window.location.reload();
|
||||
}}>
|
||||
|
|
@ -46,8 +79,7 @@ function Error(props) {
|
|||
'#oakModule': "oak-frontend-base"
|
||||
})} desc={_module ? locales.t(message, {
|
||||
'#oakModule': _module
|
||||
}) : message}>
|
||||
<ErrorMessage error={error}/>
|
||||
}) : message} content={<ErrorMessage error={error}/>}>
|
||||
<Button type="primary" onClick={async () => {
|
||||
window.location.reload();
|
||||
}}>
|
||||
|
|
@ -57,7 +89,24 @@ function Error(props) {
|
|||
</Button>
|
||||
</ErrorPage>);
|
||||
}
|
||||
return (<ErrorPage code={ECode.error} title={locales.t('error::error.title')} desc={locales.t('error::error.content')}>
|
||||
if (error?.message?.includes("Failed to fetch")) {
|
||||
return (<React.Suspense fallback={null}>
|
||||
<ErrorPage code={ECode.error} title={locales.t('error::network.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})} desc={locales.t('error::network.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})} content={<ErrorMessage error={error}/>}>
|
||||
<Button type="primary" onClick={async () => {
|
||||
window.location.reload();
|
||||
}}>
|
||||
{locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
</Button>
|
||||
</ErrorPage>
|
||||
</React.Suspense>);
|
||||
}
|
||||
return (<ErrorPage code={ECode.pageDataCacheFailure} title={locales.t('error::unknown.title')} desc={locales.t('error::unknown.content')} content={<ErrorMessage error={error}/>}>
|
||||
<Button type="primary" onClick={async () => {
|
||||
await features.localStorage.clear();
|
||||
window.location.reload();
|
||||
|
|
@ -66,20 +115,4 @@ function Error(props) {
|
|||
</Button>
|
||||
</ErrorPage>);
|
||||
}
|
||||
function ErrorMessage(props) {
|
||||
const { error } = props;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const {} = error;
|
||||
return (<span style={{
|
||||
marginBottom: 24,
|
||||
color: 'red',
|
||||
fontSize: 14,
|
||||
marginLeft: 24,
|
||||
marginRight: 24,
|
||||
}}>
|
||||
{typeof error === 'object' ? error.message : error}
|
||||
</span>);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
export default Error;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
declare class ErrorBoundary extends React.Component<{
|
||||
disabled?: boolean;
|
||||
children: React.ReactNode;
|
||||
}> {
|
||||
state: {
|
||||
hasError: boolean;
|
||||
error: undefined;
|
||||
info: undefined;
|
||||
};
|
||||
static getDerivedStateFromError(error: any): {
|
||||
hasError: boolean;
|
||||
};
|
||||
componentDidCatch(error: any, info: any): void;
|
||||
render(): string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null | undefined;
|
||||
}
|
||||
export default ErrorBoundary;
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import React, { lazy } from 'react';
|
||||
import ErrorMessage from './ErrorMessage';
|
||||
import { ECode } from '../../../types/ErrorPage';
|
||||
const ErrorPage = lazy(() => import('../../../components/errorPage'));
|
||||
class ErrorBoundary extends React.Component {
|
||||
state = {
|
||||
hasError: false,
|
||||
error: undefined,
|
||||
info: undefined,
|
||||
};
|
||||
static getDerivedStateFromError(error) {
|
||||
return { hasError: true };
|
||||
}
|
||||
componentDidCatch(error, info) {
|
||||
console.error('ErrorBoundary caught an error:', error, info);
|
||||
// 在此处可以记录错误
|
||||
if (this.props.disabled) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
error,
|
||||
info,
|
||||
});
|
||||
}
|
||||
render() {
|
||||
if (this.props.disabled) {
|
||||
return this.props.children;
|
||||
}
|
||||
if (this.state.hasError) {
|
||||
const { error } = this.state;
|
||||
// 渲染备用 UI
|
||||
const message = typeof error === 'object' ? JSON.stringify(error) : error;
|
||||
const isChunkLoadError = message?.includes('ChunkLoadError');
|
||||
return (<ErrorPage code={isChunkLoadError ? ECode.pageUpdate : ECode.pageCrash} content={<ErrorMessage error={error}/>}>
|
||||
</ErrorPage>);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
export default ErrorBoundary;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import React from 'react';
|
||||
type IErrorMessageProps = {
|
||||
error: any;
|
||||
};
|
||||
declare function ErrorMessage(props: IErrorMessageProps): React.JSX.Element | null;
|
||||
export default ErrorMessage;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
function ErrorMessage(props) {
|
||||
const { error } = props;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return (<span style={{
|
||||
marginBottom: 24,
|
||||
color: 'red',
|
||||
fontSize: 14,
|
||||
marginLeft: 24,
|
||||
marginRight: 24,
|
||||
}}>
|
||||
{typeof error === 'object' ? error.message : error}
|
||||
</span>);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
export default ErrorMessage;
|
||||
|
|
@ -4,5 +4,8 @@ export declare enum ECode {
|
|||
error = "500",
|
||||
networkError = "network-error",
|
||||
browserIncompatible = "browser-incompatible",
|
||||
maintenance = "maintenance"
|
||||
maintenance = "maintenance",
|
||||
pageCrash = "pageCrash",// 页面崩溃
|
||||
pageUpdate = "pageUpdate",// 页面更新
|
||||
pageDataCacheFailure = "pageDataCacheFailure"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,4 +6,7 @@ export var ECode;
|
|||
ECode["networkError"] = "network-error";
|
||||
ECode["browserIncompatible"] = "browser-incompatible";
|
||||
ECode["maintenance"] = "maintenance";
|
||||
ECode["pageCrash"] = "pageCrash";
|
||||
ECode["pageUpdate"] = "pageUpdate";
|
||||
ECode["pageDataCacheFailure"] = "pageDataCacheFailure";
|
||||
})(ECode || (ECode = {}));
|
||||
|
|
|
|||
|
|
@ -768,9 +768,12 @@ class ListNode extends EntityNode {
|
|||
const { data, sorter, filter } = this.constructSelection(true, false, true);
|
||||
const ids2 = ids.concat(createIds);
|
||||
/**
|
||||
* 在非modi状态下,所取数据是在ids2中满足filter的部分(自身对象和其它对象的更新可能会影响这些行不再满足条件)
|
||||
* 在非modi状态下,当前的逻辑是:
|
||||
* 若本结点非脏(其子结点也非脏),直接用ids作为查询条件(这里也隐喻着父结点不会影响这里的数据)
|
||||
* 若本结点为脏,则要考虑当前这些ids是否还满足filters,若一个结点出现这种情况要考虑,则要自己保证filter中的关联关系数据都在cache当中,不然会出现数据为空
|
||||
* by Xc 20250308
|
||||
*/
|
||||
const filter2 = inModiNextBranch ? filter : (ids2.length > 0 ? (filter ? (0, filter_1.combineFilters)(this.entity, this.cache.getSchema(), [filter, {
|
||||
const filter2 = inModiNextBranch ? filter : (ids2.length > 0 ? (filter && this.isDirty() ? (0, filter_1.combineFilters)(this.entity, this.cache.getSchema(), [filter, {
|
||||
id: {
|
||||
$in: ids2,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|||
const react_1 = tslib_1.__importStar(require("react"));
|
||||
const Message = (0, react_1.lazy)(() => Promise.resolve().then(() => tslib_1.__importStar(require('../../../components/message'))));
|
||||
const DebugPanel = (0, react_1.lazy)(() => Promise.resolve().then(() => tslib_1.__importStar(require('../../../components/func/debugPanel'))));
|
||||
const ErrorBoundary = (0, react_1.lazy)(() => Promise.resolve().then(() => tslib_1.__importStar(require('./ErrorBoundary'))));
|
||||
const AppContainer = (props) => {
|
||||
const { children } = props;
|
||||
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_1.default.Suspense, { fallback: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {}), children: (0, jsx_runtime_1.jsx)(Message, {}) }), children, (0, jsx_runtime_1.jsx)(react_1.default.Suspense, { fallback: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {}), children: process.env.NODE_ENV === 'development' && (0, jsx_runtime_1.jsx)(DebugPanel, {}) })] }));
|
||||
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_1.default.Suspense, { fallback: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {}), children: (0, jsx_runtime_1.jsx)(Message, {}) }), (0, jsx_runtime_1.jsx)(react_1.default.Suspense, { fallback: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {}), children: (0, jsx_runtime_1.jsx)(ErrorBoundary, { children: children }) }), (0, jsx_runtime_1.jsx)(react_1.default.Suspense, { fallback: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {}), children: process.env.NODE_ENV === 'development' && (0, jsx_runtime_1.jsx)(DebugPanel, {}) })] }));
|
||||
};
|
||||
exports.default = AppContainer;
|
||||
|
|
|
|||
|
|
@ -1,90 +1,90 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const tslib_1 = require("tslib");
|
||||
const jsx_runtime_1 = require("react/jsx-runtime");
|
||||
const react_1 = require("react");
|
||||
const react_1 = tslib_1.__importStar(require("react"));
|
||||
const antd_1 = require("antd");
|
||||
const Exception_1 = require("oak-domain/lib/types/Exception");
|
||||
const ErrorMessage_1 = tslib_1.__importDefault(require("./ErrorMessage"));
|
||||
const ErrorPage_1 = require("../../../types/ErrorPage");
|
||||
const ErrorPage = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('../../../components/errorPage'))));
|
||||
const ErrorPage = (0, react_1.lazy)(() => Promise.resolve().then(() => tslib_1.__importStar(require('../../../components/errorPage'))));
|
||||
function Error(props) {
|
||||
const { error, features } = props;
|
||||
const { locales } = features;
|
||||
if (error instanceof Exception_1.OakException) {
|
||||
if (error instanceof Exception_1.OakNetworkException) {
|
||||
// 网络中断出现的异常
|
||||
return ((0, jsx_runtime_1.jsxs)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::network.title', {
|
||||
return ((0, jsx_runtime_1.jsx)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::network.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), desc: locales.t('error::network.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), children: [(0, jsx_runtime_1.jsx)(ErrorMessage, { error: error }), (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
window.location.reload();
|
||||
}, children: locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}) })] }));
|
||||
}
|
||||
if (error instanceof Exception_1.OakServerProxyException) {
|
||||
// 服务器代理异常
|
||||
return ((0, jsx_runtime_1.jsxs)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::proxy.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), desc: locales.t('error::proxy.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), children: [(0, jsx_runtime_1.jsx)(ErrorMessage, { error: error }), (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
window.location.reload();
|
||||
}, children: locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}) })] }));
|
||||
}
|
||||
const { _module, message } = error;
|
||||
return ((0, jsx_runtime_1.jsxs)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::error.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), desc: _module ? locales.t(message, {
|
||||
'#oakModule': _module
|
||||
}) : message, children: [(0, jsx_runtime_1.jsx)(ErrorMessage, { error: error }), (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
}), content: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error }), children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
window.location.reload();
|
||||
}, children: locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}) })] }));
|
||||
}) }) }));
|
||||
}
|
||||
if (error instanceof Exception_1.OakServerProxyException) {
|
||||
// 服务器代理异常
|
||||
return ((0, jsx_runtime_1.jsx)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::proxy.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), desc: locales.t('error::proxy.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), content: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error }), children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
window.location.reload();
|
||||
}, children: locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}) }) }));
|
||||
}
|
||||
if (error instanceof Exception_1.OakClockDriftException) {
|
||||
// 服务器加密报错
|
||||
return ((0, jsx_runtime_1.jsx)(react_1.default.Suspense, { fallback: null, children: (0, jsx_runtime_1.jsx)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::clockDrift.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), desc: locales.t('error::clockDrift.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), content: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error }), children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
window.location.reload();
|
||||
}, children: locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}) }) }) }));
|
||||
}
|
||||
if (error instanceof Exception_1.OakRequestTimeoutException) {
|
||||
// 服务器超时报错
|
||||
return ((0, jsx_runtime_1.jsx)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::timeout.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), desc: locales.t('error::timeout.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), content: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error }), children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
window.location.reload();
|
||||
}, children: locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}) }) }));
|
||||
}
|
||||
const { _module, message } = error;
|
||||
return ((0, jsx_runtime_1.jsx)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::error.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), desc: _module ? locales.t(message, {
|
||||
'#oakModule': _module
|
||||
}) : message, content: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error }), children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
window.location.reload();
|
||||
}, children: locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}) }) }));
|
||||
}
|
||||
return ((0, jsx_runtime_1.jsx)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::error.title'), desc: locales.t('error::error.content'), children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
if (error?.message?.includes("Failed to fetch")) {
|
||||
return ((0, jsx_runtime_1.jsx)(react_1.default.Suspense, { fallback: null, children: (0, jsx_runtime_1.jsx)(ErrorPage, { code: ErrorPage_1.ECode.error, title: locales.t('error::network.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), desc: locales.t('error::network.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}), content: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error }), children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
window.location.reload();
|
||||
}, children: locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
}) }) }) }));
|
||||
}
|
||||
return ((0, jsx_runtime_1.jsx)(ErrorPage, { code: ErrorPage_1.ECode.pageDataCacheFailure, title: locales.t('error::unknown.title'), desc: locales.t('error::unknown.content'), content: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error }), children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: async () => {
|
||||
await features.localStorage.clear();
|
||||
window.location.reload();
|
||||
}, children: locales.t('clearCache') }) }));
|
||||
}
|
||||
function ErrorMessage(props) {
|
||||
const { error } = props;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const {} = error;
|
||||
return ((0, jsx_runtime_1.jsx)("span", { style: {
|
||||
marginBottom: 24,
|
||||
color: 'red',
|
||||
fontSize: 14,
|
||||
marginLeft: 24,
|
||||
marginRight: 24,
|
||||
}, children: typeof error === 'object' ? error.message : error }));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.default = Error;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
declare class ErrorBoundary extends React.Component<{
|
||||
disabled?: boolean;
|
||||
children: React.ReactNode;
|
||||
}> {
|
||||
state: {
|
||||
hasError: boolean;
|
||||
error: undefined;
|
||||
info: undefined;
|
||||
};
|
||||
static getDerivedStateFromError(error: any): {
|
||||
hasError: boolean;
|
||||
};
|
||||
componentDidCatch(error: any, info: any): void;
|
||||
render(): string | number | boolean | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
||||
}
|
||||
export default ErrorBoundary;
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const tslib_1 = require("tslib");
|
||||
const jsx_runtime_1 = require("react/jsx-runtime");
|
||||
const react_1 = tslib_1.__importStar(require("react"));
|
||||
const ErrorMessage_1 = tslib_1.__importDefault(require("./ErrorMessage"));
|
||||
const ErrorPage_1 = require("../../../types/ErrorPage");
|
||||
const ErrorPage = (0, react_1.lazy)(() => Promise.resolve().then(() => tslib_1.__importStar(require('../../../components/errorPage'))));
|
||||
class ErrorBoundary extends react_1.default.Component {
|
||||
state = {
|
||||
hasError: false,
|
||||
error: undefined,
|
||||
info: undefined,
|
||||
};
|
||||
static getDerivedStateFromError(error) {
|
||||
return { hasError: true };
|
||||
}
|
||||
componentDidCatch(error, info) {
|
||||
console.error('ErrorBoundary caught an error:', error, info);
|
||||
// 在此处可以记录错误
|
||||
if (this.props.disabled) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
error,
|
||||
info,
|
||||
});
|
||||
}
|
||||
render() {
|
||||
if (this.props.disabled) {
|
||||
return this.props.children;
|
||||
}
|
||||
if (this.state.hasError) {
|
||||
const { error } = this.state;
|
||||
// 渲染备用 UI
|
||||
const message = typeof error === 'object' ? JSON.stringify(error) : error;
|
||||
const isChunkLoadError = message?.includes('ChunkLoadError');
|
||||
return ((0, jsx_runtime_1.jsx)(ErrorPage, { code: isChunkLoadError ? ErrorPage_1.ECode.pageUpdate : ErrorPage_1.ECode.pageCrash, content: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error }) }));
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
exports.default = ErrorBoundary;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
type IErrorMessageProps = {
|
||||
error: any;
|
||||
};
|
||||
declare function ErrorMessage(props: IErrorMessageProps): import("react/jsx-runtime").JSX.Element | null;
|
||||
export default ErrorMessage;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const jsx_runtime_1 = require("react/jsx-runtime");
|
||||
function ErrorMessage(props) {
|
||||
const { error } = props;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return ((0, jsx_runtime_1.jsx)("span", { style: {
|
||||
marginBottom: 24,
|
||||
color: 'red',
|
||||
fontSize: 14,
|
||||
marginLeft: 24,
|
||||
marginRight: 24,
|
||||
}, children: typeof error === 'object' ? error.message : error }));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.default = ErrorMessage;
|
||||
|
|
@ -4,5 +4,8 @@ export declare enum ECode {
|
|||
error = "500",
|
||||
networkError = "network-error",
|
||||
browserIncompatible = "browser-incompatible",
|
||||
maintenance = "maintenance"
|
||||
maintenance = "maintenance",
|
||||
pageCrash = "pageCrash",// 页面崩溃
|
||||
pageUpdate = "pageUpdate",// 页面更新
|
||||
pageDataCacheFailure = "pageDataCacheFailure"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,4 +9,7 @@ var ECode;
|
|||
ECode["networkError"] = "network-error";
|
||||
ECode["browserIncompatible"] = "browser-incompatible";
|
||||
ECode["maintenance"] = "maintenance";
|
||||
ECode["pageCrash"] = "pageCrash";
|
||||
ECode["pageUpdate"] = "pageUpdate";
|
||||
ECode["pageDataCacheFailure"] = "pageDataCacheFailure";
|
||||
})(ECode || (exports.ECode = ECode = {}));
|
||||
|
|
|
|||
|
|
@ -35,6 +35,21 @@ const DefaultErrorInfo = {
|
|||
desc: '抱歉,您正在使用的浏览器版本过低,无法打开当前网页。',
|
||||
imagePath: './assets/svg/assets-result-browser-incompatible.svg',
|
||||
},
|
||||
[ECode.pageCrash]: {
|
||||
title: '抱歉,网页崩溃了',
|
||||
desc: '网页似乎遇到了问题,我们将尽快解决,请稍后重新加载。',
|
||||
imagePath: './assets/svg/assets-result-404.svg',
|
||||
},
|
||||
[ECode.pageUpdate]: {
|
||||
title: '网页有更新',
|
||||
desc: '检查到网页有更新,请刷新以查看最新内容。',
|
||||
imagePath: './assets/svg/assets-result-404.svg',
|
||||
},
|
||||
[ECode.pageDataCacheFailure]: {
|
||||
title: '数据缓存失效',
|
||||
desc: '非常抱歉,数据缓存已失效。请点击【清除缓存】以更新信息。',
|
||||
imagePath: './assets/svg/assets-result-404.svg',
|
||||
},
|
||||
};
|
||||
|
||||
export default OakComponent({
|
||||
|
|
@ -72,6 +87,9 @@ export default OakComponent({
|
|||
goBack(delta?: number) {
|
||||
this.navigateBack(delta);
|
||||
},
|
||||
async clearData() {
|
||||
await this.features.localStorage.clear();
|
||||
}
|
||||
},
|
||||
}) as <ED2 extends ED, T2 extends keyof ED2>(
|
||||
props: ReactComponentProps<
|
||||
|
|
@ -84,6 +102,7 @@ export default OakComponent({
|
|||
desc?: string;
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
content?: React.ReactNode;
|
||||
}
|
||||
>
|
||||
) => React.ReactElement;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ interface IErrorPageProps {
|
|||
desc?: string;
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
content?: React.ReactNode;
|
||||
}
|
||||
|
||||
const errorInfo = {
|
||||
|
|
@ -52,6 +53,21 @@ const errorInfo = {
|
|||
desc: '系统维护中,请稍后再试。',
|
||||
icon: <LightMaintenanceIcon />,
|
||||
},
|
||||
[ECode.pageCrash]: {
|
||||
title: '抱歉,网页崩溃了',
|
||||
desc: '网页似乎遇到了问题,我们将尽快解决,请稍后重新加载。',
|
||||
icon: <Light404Icon />,
|
||||
},
|
||||
[ECode.pageUpdate]: {
|
||||
title: '网页有更新',
|
||||
desc: '检查到网页有更新,请刷新以查看最新内容。',
|
||||
icon: <Light404Icon />,
|
||||
},
|
||||
[ECode.pageDataCacheFailure]: {
|
||||
title: '数据缓存失效',
|
||||
desc: '非常抱歉,数据缓存已失效。请点击【清除缓存】以更新信息。',
|
||||
icon: <Light404Icon />,
|
||||
},
|
||||
};
|
||||
|
||||
export default function Render(
|
||||
|
|
@ -62,13 +78,16 @@ export default function Render(
|
|||
IErrorPageProps,
|
||||
{
|
||||
goBack: (delta?: number) => void;
|
||||
clearData: () => Promise<void>;
|
||||
}
|
||||
>
|
||||
) {
|
||||
const { code, icon, title, desc, children } = props.data;
|
||||
const { t, goBack } = props.methods;
|
||||
const { code, icon, title, desc, children, content } = props.data;
|
||||
const { t, goBack, clearData } = props.methods;
|
||||
const info = errorInfo[code];
|
||||
const prefixCls = 'oak';
|
||||
const backed = code === ECode.notFound || code === ECode.forbidden;
|
||||
|
||||
|
||||
return (
|
||||
<div className={`${prefixCls}-errorPage`}>
|
||||
|
|
@ -79,14 +98,41 @@ export default function Render(
|
|||
<div className={`${prefixCls}-errorPage__description`}>
|
||||
{desc || info?.desc}
|
||||
</div>
|
||||
{content}
|
||||
{children || (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
goBack();
|
||||
switch (code) {
|
||||
case ECode.forbidden:
|
||||
case ECode.notFound: {
|
||||
goBack();
|
||||
break;
|
||||
}
|
||||
case ECode.pageDataCacheFailure: {
|
||||
clearData();
|
||||
window.location.reload();
|
||||
break;
|
||||
}
|
||||
case ECode.networkError:
|
||||
case ECode.browserIncompatible:
|
||||
case ECode.maintenance:
|
||||
case ECode.pageCrash:
|
||||
case ECode.pageUpdate:
|
||||
default: {
|
||||
window.location.reload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
返回
|
||||
{backed
|
||||
? t('common::back', {
|
||||
'#oakModule': 'oak-frontend-base',
|
||||
})
|
||||
: t('common::refresh', {
|
||||
'#oakModule': 'oak-frontend-base',
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -107,11 +107,7 @@ export default function Render<ED2 extends ED>(
|
|||
label:
|
||||
typeof ele.value === 'boolean'
|
||||
? t(`${ele.value ? 'tip.yes' : 'tip.no'}`)
|
||||
: t(
|
||||
`${entityI18n as string}:v.${attrI18n}.${
|
||||
ele.value
|
||||
}`
|
||||
),
|
||||
: t(`${entityI18n as string}:v.${attrI18n}.${ele.value}`),
|
||||
value: ele.value,
|
||||
}));
|
||||
const multiple = ['$in', '$nin'].includes(op || '');
|
||||
|
|
@ -139,8 +135,6 @@ export default function Render<ED2 extends ED>(
|
|||
case 'DatePicker': {
|
||||
const { dateProps } = column;
|
||||
const { showTime = false } = dateProps || {};
|
||||
//assert(op, '选择时间,算子必须传入');
|
||||
const unitOfTime = 'day';
|
||||
V = (
|
||||
<DatePicker
|
||||
placeholder={placeholder || t('placeholder.select')}
|
||||
|
|
@ -200,9 +194,3 @@ export default function Render<ED2 extends ED>(
|
|||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
||||
function assertMessage(attr: string, attrType: string, op: Ops, ops: Ops[]) {
|
||||
return `attr为【${attr}】, 传入的算子【${op}】不支持,类型【${attrType}】只支持【${JSON.stringify(
|
||||
ops
|
||||
)}】`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ export default OakComponent({
|
|||
},
|
||||
formData() {
|
||||
const { multiple, entityIds, pickerRender } = this.props;
|
||||
const { entity, projection, title } =
|
||||
pickerRender as OakAbsRefAttrPickerRender<ED, keyof ED>;
|
||||
const { entity, projection, title } = pickerRender as OakAbsRefAttrPickerRender<ED, keyof ED>;
|
||||
const rows =
|
||||
entityIds &&
|
||||
entityIds.length &&
|
||||
|
|
@ -33,8 +32,7 @@ export default OakComponent({
|
|||
},
|
||||
},
|
||||
});
|
||||
const renderValue =
|
||||
rows && rows.length ? rows.map((row) => title(row)).join(',') : '';
|
||||
const renderValue = rows && rows.length ? rows.map((row) => title(row)).join(',') : '';
|
||||
const schema = this.features.cache.getSchema();
|
||||
return {
|
||||
renderValue,
|
||||
|
|
@ -60,8 +58,7 @@ export default OakComponent({
|
|||
methods: {
|
||||
async refreshData() {
|
||||
const { pickerRender, multiple } = this.props;
|
||||
const { mode, entity, projection, filter, count, title } =
|
||||
pickerRender as OakAbsRefAttrPickerDef<ED, keyof ED>;
|
||||
const { mode, entity, projection, filter, count, title } = pickerRender as OakAbsRefAttrPickerDef<ED, keyof ED>;
|
||||
if (mode === 'radio') {
|
||||
// radio的要先取数据出来
|
||||
assert(
|
||||
|
|
@ -78,8 +75,7 @@ export default OakComponent({
|
|||
return;
|
||||
}
|
||||
|
||||
const proj =
|
||||
typeof projection === 'function' ? projection() : projection;
|
||||
const proj = typeof projection === 'function' ? projection() : projection;
|
||||
const filter2 = typeof filter === 'function' ? filter() : filter;
|
||||
const { data } = await this.features.cache.refresh(entity, {
|
||||
data: proj,
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ export default function render(
|
|||
options={data!.map((ele) => ({
|
||||
value: ele.id,
|
||||
label: ele.title,
|
||||
key: ele.id,
|
||||
}))}
|
||||
value={entityIds}
|
||||
onChange={(value) => onChange(value as string[])}
|
||||
|
|
@ -102,6 +103,7 @@ export default function render(
|
|||
options={data!.map((ele) => ({
|
||||
value: ele.id,
|
||||
label: ele.title,
|
||||
key: ele.id,
|
||||
}))}
|
||||
></Radio.Group>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ export default function render(
|
|||
options={data!.map((ele) => ({
|
||||
value: ele.id,
|
||||
label: ele.title,
|
||||
key: ele.id,
|
||||
}))}
|
||||
multiple={multiple}
|
||||
></Selector>
|
||||
|
|
@ -80,7 +81,7 @@ export default function render(
|
|||
onChange={(value) => onChange(value as string[])}
|
||||
>
|
||||
{data!.map((ele) => (
|
||||
<Checkbox value={ele.id}>{ele.title}</Checkbox>
|
||||
<Checkbox key={ele.id} value={ele.id}>{ele.title}</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
);
|
||||
|
|
@ -91,7 +92,7 @@ export default function render(
|
|||
value={entityId}
|
||||
>
|
||||
{data!.map((ele) => (
|
||||
<Radio value={ele.id}>{ele.title}</Radio>
|
||||
<Radio key={ele.id} value={ele.id}>{ele.title}</Radio>
|
||||
))}
|
||||
</Radio.Group>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,5 +18,21 @@
|
|||
"unknown": {
|
||||
"title": "未知异常",
|
||||
"content": "抱歉,出现未知异常,可能是数据缓存失效,需要清除缓存,请点击【清除缓存】再尝试"
|
||||
},
|
||||
"timeout": {
|
||||
"title": "请求超时",
|
||||
"content": "非常抱歉,请求超时。请稍后再试"
|
||||
},
|
||||
"clockDrift": {
|
||||
"title": "网络请求错误",
|
||||
"content": "非常抱歉,网络请求出现了问题。请稍后再试,我们正在努力修复"
|
||||
},
|
||||
"pageCrash": {
|
||||
"title": "抱歉,网页崩溃了",
|
||||
"content": "网页似乎遇到了问题,我们将尽快解决,请稍后重新加载"
|
||||
},
|
||||
"pageUpdate": {
|
||||
"title": "网页有更新",
|
||||
"content": "检查到网页有更新,请刷新以查看最新内容"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { lazy } from 'react';
|
||||
const Message = lazy(() => import('../../../components/message'));
|
||||
const DebugPanel = lazy(() => import('../../../components/func/debugPanel'));
|
||||
const ErrorBoundary = lazy(() => import('./ErrorBoundary'));
|
||||
|
||||
type AppContainerProps = {
|
||||
children?: React.ReactNode
|
||||
|
|
@ -13,7 +14,11 @@ const AppContainer = (props: AppContainerProps) => {
|
|||
<React.Suspense fallback={<></>}>
|
||||
<Message />
|
||||
</React.Suspense>
|
||||
{children}
|
||||
<React.Suspense fallback={<></>}>
|
||||
<ErrorBoundary>
|
||||
{children}
|
||||
</ErrorBoundary>
|
||||
</React.Suspense>
|
||||
<React.Suspense fallback={<></>}>
|
||||
{process.env.NODE_ENV === 'development' && <DebugPanel />}
|
||||
</React.Suspense>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|||
|
||||
import {
|
||||
OakException,
|
||||
OakExternalException,
|
||||
OakNetworkException,
|
||||
OakServerProxyException,
|
||||
OakClockDriftException,
|
||||
OakRequestTimeoutException,
|
||||
} from 'oak-domain/lib/types/Exception';
|
||||
import ErrorMessage from './ErrorMessage';
|
||||
import { ECode } from '../../../types/ErrorPage';
|
||||
import { BasicFeatures } from '../../../features';
|
||||
const ErrorPage = lazy(() => import('../../../components/errorPage'));
|
||||
|
|
@ -34,8 +36,8 @@ function Error<ED extends EntityDict & BaseEntityDict>(props: ErrorProps<ED>) {
|
|||
desc={locales.t('error::network.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
content={<ErrorMessage error={error} />}
|
||||
>
|
||||
<ErrorMessage error={error} />
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
|
|
@ -60,8 +62,64 @@ function Error<ED extends EntityDict & BaseEntityDict>(props: ErrorProps<ED>) {
|
|||
desc={locales.t('error::proxy.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
content={<ErrorMessage error={error} />}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
window.location.reload();
|
||||
}}
|
||||
>
|
||||
{locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
</Button>
|
||||
</ErrorPage>
|
||||
);
|
||||
}
|
||||
|
||||
if (error instanceof OakClockDriftException) {
|
||||
// 服务器加密报错
|
||||
return (
|
||||
<React.Suspense fallback={null}>
|
||||
<ErrorPage
|
||||
code={ECode.error}
|
||||
title={locales.t('error::clockDrift.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
desc={locales.t('error::clockDrift.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
content={<ErrorMessage error={error} />}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
window.location.reload();
|
||||
}}
|
||||
>
|
||||
{locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
</Button>
|
||||
</ErrorPage>
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
if (error instanceof OakRequestTimeoutException) {
|
||||
// 服务器超时报错
|
||||
return (
|
||||
<ErrorPage
|
||||
code={ECode.error}
|
||||
title={locales.t('error::timeout.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
desc={locales.t('error::timeout.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
content={<ErrorMessage error={error} />}
|
||||
>
|
||||
<ErrorMessage error={error} />
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
|
|
@ -86,8 +144,8 @@ function Error<ED extends EntityDict & BaseEntityDict>(props: ErrorProps<ED>) {
|
|||
desc={_module ? locales.t(message, {
|
||||
'#oakModule': _module
|
||||
}) : message}
|
||||
content={<ErrorMessage error={error} />}
|
||||
>
|
||||
<ErrorMessage error={error} />
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
|
|
@ -102,11 +160,40 @@ function Error<ED extends EntityDict & BaseEntityDict>(props: ErrorProps<ED>) {
|
|||
);
|
||||
}
|
||||
|
||||
if (error?.message?.includes("Failed to fetch")) {
|
||||
return (
|
||||
<React.Suspense fallback={null}>
|
||||
<ErrorPage
|
||||
code={ECode.error}
|
||||
title={locales.t('error::network.title', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
desc={locales.t('error::network.content', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
content={<ErrorMessage error={error} />}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
window.location.reload();
|
||||
}}
|
||||
>
|
||||
{locales.t('common::refresh', {
|
||||
'#oakModule': "oak-frontend-base"
|
||||
})}
|
||||
</Button>
|
||||
</ErrorPage>
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorPage
|
||||
code={ECode.error}
|
||||
title={locales.t('error::error.title')}
|
||||
desc={locales.t('error::error.content')}
|
||||
code={ECode.pageDataCacheFailure}
|
||||
title={locales.t('error::unknown.title')}
|
||||
desc={locales.t('error::unknown.content')}
|
||||
content={<ErrorMessage error={error} />}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
|
|
@ -121,25 +208,4 @@ function Error<ED extends EntityDict & BaseEntityDict>(props: ErrorProps<ED>) {
|
|||
);
|
||||
}
|
||||
|
||||
function ErrorMessage(props: { error: any }) {
|
||||
const { error } = props;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const {} = error;
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
color: 'red',
|
||||
fontSize: 14,
|
||||
marginLeft: 24,
|
||||
marginRight: 24,
|
||||
}}
|
||||
>
|
||||
{typeof error === 'object' ? error.message : error}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export default Error;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
import React, { lazy } from 'react';
|
||||
|
||||
import ErrorMessage from './ErrorMessage';
|
||||
import { ECode } from '../../../types/ErrorPage';
|
||||
const ErrorPage = lazy(() => import('../../../components/errorPage'));
|
||||
|
||||
class ErrorBoundary extends React.Component<{ disabled?: boolean; children: React.ReactNode }> {
|
||||
state = {
|
||||
hasError: false,
|
||||
error: undefined,
|
||||
info: undefined,
|
||||
};
|
||||
|
||||
static getDerivedStateFromError(error: any) {
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error: any, info: any) {
|
||||
console.error('ErrorBoundary caught an error:', error, info);
|
||||
// 在此处可以记录错误
|
||||
if (this.props.disabled) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
error,
|
||||
info,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.disabled) {
|
||||
return this.props.children;
|
||||
}
|
||||
if (this.state.hasError) {
|
||||
const { error } = this.state;
|
||||
// 渲染备用 UI
|
||||
const message = typeof error === 'object' ? JSON.stringify(error) : error;
|
||||
const isChunkLoadError = (message as string)?.includes('ChunkLoadError');
|
||||
return (
|
||||
<ErrorPage
|
||||
code={isChunkLoadError ? ECode.pageUpdate : ECode.pageCrash}
|
||||
content={<ErrorMessage error={error} />}
|
||||
>
|
||||
</ErrorPage>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react';
|
||||
|
||||
type IErrorMessageProps = {
|
||||
error: any;
|
||||
}
|
||||
|
||||
function ErrorMessage(props: IErrorMessageProps) {
|
||||
const { error } = props;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
color: 'red',
|
||||
fontSize: 14,
|
||||
marginLeft: 24,
|
||||
marginRight: 24,
|
||||
}}
|
||||
>
|
||||
{typeof error === 'object' ? error.message : error}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
export default ErrorMessage;
|
||||
|
|
@ -8,4 +8,8 @@ export enum ECode {
|
|||
networkError = 'network-error',
|
||||
browserIncompatible = 'browser-incompatible',
|
||||
maintenance = 'maintenance',
|
||||
|
||||
pageCrash = 'pageCrash', // 页面崩溃
|
||||
pageUpdate = 'pageUpdate', // 页面更新
|
||||
pageDataCacheFailure = 'pageDataCacheFailure', // 页面数据缓存失效
|
||||
}
|
||||
Loading…
Reference in New Issue