diff --git a/react-ui/src/app.tsx b/react-ui/src/app.tsx index ff14f8c9..e6a09fb5 100644 --- a/react-ui/src/app.tsx +++ b/react-ui/src/app.tsx @@ -22,6 +22,7 @@ export { requestConfig as request } from './requestConfig'; // const isDev = process.env.NODE_ENV === 'development'; import { type GlobalInitialState } from '@/types'; import { menuItemRender } from '@/utils/menuRender'; +import ErrorBoundary from './components/ErrorBoundary'; import { gotoLoginPage } from './utils/ui'; /** @@ -65,6 +66,7 @@ export async function getInitialState(): Promise { // ProLayout 支持的api https://procomponents.ant.design/components/layout export const layout: RuntimeConfig['layout'] = ({ initialState }) => { return { + ErrorBoundary: ErrorBoundary, rightContentRender: false, waterMarkProps: { // content: initialState?.currentUser?.nickName, diff --git a/react-ui/src/assets/img/404.png b/react-ui/src/assets/img/404.png index 610b7986..21e86122 100644 Binary files a/react-ui/src/assets/img/404.png and b/react-ui/src/assets/img/404.png differ diff --git a/react-ui/src/assets/img/missing-back.png b/react-ui/src/assets/img/missing-back.png index 850651ce..5d9ad049 100644 Binary files a/react-ui/src/assets/img/missing-back.png and b/react-ui/src/assets/img/missing-back.png differ diff --git a/react-ui/src/assets/img/no-data.png b/react-ui/src/assets/img/no-data.png index d2239289..9ca6aead 100644 Binary files a/react-ui/src/assets/img/no-data.png and b/react-ui/src/assets/img/no-data.png differ diff --git a/react-ui/src/components/ErrorBoundary/index.tsx b/react-ui/src/components/ErrorBoundary/index.tsx index e69de29b..66bc8d94 100644 --- a/react-ui/src/components/ErrorBoundary/index.tsx +++ b/react-ui/src/components/ErrorBoundary/index.tsx @@ -0,0 +1,78 @@ +import KFEmpty, { EmptyType } from '@/components/KFEmpty'; +import { Button } from 'antd'; +import { Component, ReactNode } from 'react'; + +interface ErrorBoundaryProps { + children: ReactNode; + fallback?: ReactNode; // Optional fallback UI to show in case of error +} + +interface ErrorBoundaryState { + hasError: boolean; + error: Error | null; +} + +class ErrorBoundary extends Component { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { + hasError: false, + error: null, + }; + } + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + // Update state so the next render shows the fallback UI + return { hasError: true, error }; + } + + // componentDidCatch(error: Error, errorInfo: ErrorInfo) { + // // You can log the error to an error reporting service here + // console.error('Error caught by ErrorBoundary:', error.message, errorInfo.componentStack); + // } + + render() { + if (this.state.hasError) { + return this.props.fallback || ; + } + return this.props.children; + } +} + +function ErrorBoundaryFallback({ error }: { error: Error | null }) { + const message = error && error instanceof Error ? error.message : 'Unknown error'; + const errorMsg = + process.env.NODE_ENV === 'development' ? message : '非常抱歉,程序运行错误,\n我们会尽快修复。'; + return ( + { + return ( + <> + + + + ); + }} + > + ); +} + +export default ErrorBoundary; diff --git a/react-ui/src/components/KFEmpty/index.less b/react-ui/src/components/KFEmpty/index.less index e62edff5..39f70281 100644 --- a/react-ui/src/components/KFEmpty/index.less +++ b/react-ui/src/components/KFEmpty/index.less @@ -18,6 +18,7 @@ } &__content { + max-width: 50%; margin-top: 15px; color: @text-color-secondary; font-size: 15px; diff --git a/react-ui/src/components/KFEmpty/index.tsx b/react-ui/src/components/KFEmpty/index.tsx index e9bc79e1..e36a4b2d 100644 --- a/react-ui/src/components/KFEmpty/index.tsx +++ b/react-ui/src/components/KFEmpty/index.tsx @@ -37,7 +37,7 @@ function KFEmpty({ type, title, content, - hasFooter = false, + hasFooter = true, footer, buttonTitle = '刷新', onRefresh, diff --git a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx index 65446268..08549ab6 100644 --- a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx +++ b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx @@ -1,146 +1,159 @@ import BasicInfo, { BasicInfoData } from '@/components/BasicInfo'; import SubAreaTitle from '@/components/SubAreaTitle'; -import { ResourceData, ResourceType } from '@/pages/Dataset/config'; +import { DatasetData, ModelData, ResourceType } from '@/pages/Dataset/config'; import styles from './index.less'; type ResourceIntroProps = { resourceType: ResourceType; - info: ResourceData; + info: DatasetData | ModelData; }; -const formatArray = (arr?: string[]) => { +// const formatArray = (arr?: ResourceData[]) => { +// if (!arr || arr.length === 0) { +// return '--'; +// } +// return arr.map((item) => item.name).join('\n'); +// }; + +const formatDataset = (arr?: DatasetData[]) => { if (!arr || arr.length === 0) { - return '--'; + return undefined; } - return arr.join('\n'); + return arr.map((item) => item.name).join('\n'); }; const formatMap = (map?: Record) => { if (!map || Object.keys(map).length === 0) { - return '--'; + return undefined; } return Object.entries(map) .map(([key, value]) => `${key} = ${value}`) .join('\n'); }; -function ResourceIntro({ resourceType, info }: ResourceIntroProps) { - const datasetDatas: BasicInfoData[] = [ - { - label: '数据集名称', - value: info.name, - }, - { - label: '版本', - value: info.version, - }, - { - label: '创建人', - value: info.create_by, - }, - { - label: '更新时间', - value: info.update_time, - }, - { - label: '数据来源', - value: info.dataset_source, - }, - { - label: '处理代码', - value: info.processing_code, - }, - { - label: '数据集分类', - value: info.data_type, - }, - { - label: '研究方向', - value: info.data_tag, - }, - { - label: '数据集描述', - value: info.description, - }, - { - label: '版本描述', - value: info.version_desc, - }, - ]; +const getDatasetDatas = (data: DatasetData): BasicInfoData[] => [ + { + label: '数据集名称', + value: data.name, + }, + { + label: '版本', + value: data.version, + }, + { + label: '创建人', + value: data.create_by, + }, + { + label: '更新时间', + value: data.update_time, + }, + { + label: '数据来源', + value: data.dataset_source, + }, + { + label: '处理代码', + value: data.processing_code, + }, + { + label: '数据集分类', + value: data.data_type, + }, + { + label: '研究方向', + value: data.data_tag, + }, + { + label: '数据集描述', + value: data.description, + }, + { + label: '版本描述', + value: data.version_desc, + }, +]; - const modelDatas: BasicInfoData[] = [ - { - label: '模型名称', - value: info.name, - }, - { - label: '版本', - value: info.version, - }, - { - label: '创建人', - value: info.create_by, - }, - { - label: '更新时间', - value: info.update_time, - }, - { - label: '训练镜像', - value: info.image, - }, - { - label: '训练代码', - value: info.code, - }, - { - label: '训练数据集', - value: info.train_datasets, - format: formatArray, - }, - { - label: '测试数据集', - value: info.test_datasets, - format: formatArray, - }, - { - label: '参数', - value: info.params, - format: formatMap, - }, - { - label: '指标', - value: info.metrics, - format: formatMap, - }, - { - label: '训练任务', - value: info.train_task, - }, - { - label: '模型来源', - value: info.model_source, - }, - { - label: '模型框架', - value: info.model_type, - }, - { - label: '模型能力', - value: info.model_tag, - }, - { - label: '模型描述', - value: info.description, - }, - { - label: '版本描述', - value: info.version_desc, - }, - ]; +const getModelDatas = (data: ModelData): BasicInfoData[] => [ + { + label: '模型名称', + value: data.name, + }, + { + label: '版本', + value: data.version, + }, + { + label: '创建人', + value: data.create_by, + }, + { + label: '更新时间', + value: data.update_time, + }, + { + label: '训练镜像', + value: data.image, + }, + { + label: '训练代码', + value: data.code, + }, + { + label: '训练数据集', + value: data.train_datasets, + format: formatDataset, + }, + { + label: '测试数据集', + value: data.test_datasets, + format: formatDataset, + }, + { + label: '参数', + value: data.params, + format: formatMap, + }, + { + label: '指标', + value: data.metrics, + format: formatMap, + }, + { + label: '训练任务', + value: data.train_task, + format: (value?: any) => value?.name, + externalLink: data.train_task + ? `${location.origin}/pipeline/experiment/instance/${data.train_task.task_id}/${data.train_task.ins_id}` + : '', + }, + { + label: '模型来源', + value: data.model_source, + }, + { + label: '模型框架', + value: data.model_type, + }, + { + label: '模型能力', + value: data.model_tag, + }, + { + label: '模型描述', + value: data.description, + }, + { + label: '版本描述', + value: data.version_desc, + }, +]; +function ResourceIntro({ resourceType, info }: ResourceIntroProps) { const basicDatas: BasicInfoData[] = - resourceType === ResourceType.Dataset ? datasetDatas : modelDatas; + resourceType === ResourceType.Dataset + ? getDatasetDatas(info as DatasetData) + : getModelDatas(info as ModelData); return (
diff --git a/react-ui/src/pages/Dataset/config.tsx b/react-ui/src/pages/Dataset/config.tsx index b1677bc3..5a9fb008 100644 --- a/react-ui/src/pages/Dataset/config.tsx +++ b/react-ui/src/pages/Dataset/config.tsx @@ -128,7 +128,7 @@ export type CategoryData = { }; // 数据集、模型列表数据 -export type ResourceData = { +export interface ResourceData { id: number; name: string; identifier: string; @@ -142,25 +142,34 @@ export type ResourceData = { version_desc?: string; usage?: string; relative_paths?: string; - // 数据集 + resourceType: ResourceType.Dataset | ResourceType.Model; +} + +// 数据集数据 +export interface DatasetData extends ResourceData { + resourceType: ResourceType.Dataset; // 用于区别类型 data_type?: string; // 数据集分类 data_tag?: string; // 研究方向 processing_code?: string; // 处理代码 dataset_source?: string; // 数据来源 - dataset_version_vos: ResourceFileData[]; - // 模型 + dataset_version_vos?: ResourceFileData[]; +} + +// 模型数据 +export interface ModelData extends ResourceData { + resourceType: ResourceType.Model; // 用于区别类型 model_type?: string; // 模型框架 model_tag?: string; // 模型能力 image?: string; // 训练镜像 code?: string; // 训练镜像 - train_datasets?: string[]; // 训练数据集 - test_datasets?: string[]; // 测试数据集 + train_datasets?: DatasetData[]; // 训练数据集 + test_datasets?: DatasetData[]; // 测试数据集 params?: Record; // 参数 metrics?: Record; // 指标 - train_task?: string; // 训练任务 + train_task?: TrainTask; // 训练任务 model_source?: string; // 模型来源 - model_version_vos: ResourceFileData[]; -}; + model_version_vos?: ResourceFileData[]; +} // 版本数据 export type ResourceVersionData = { @@ -177,3 +186,10 @@ export type ResourceFileData = { url: string; update_time?: string; }; + +// 训练任务 +export type TrainTask = { + ins_id: number; + name: string; + task_id: string; +}; diff --git a/react-ui/src/pages/Model/components/ModelEvolution/utils.tsx b/react-ui/src/pages/Model/components/ModelEvolution/utils.tsx index d1c3ca15..c4511e17 100644 --- a/react-ui/src/pages/Model/components/ModelEvolution/utils.tsx +++ b/react-ui/src/pages/Model/components/ModelEvolution/utils.tsx @@ -1,3 +1,4 @@ +import { TrainTask } from '@/pages/Dataset/config'; import { changePropertyName, fittingString } from '@/utils'; import { EdgeConfig, GraphData, LayoutConfig, NodeConfig, TreeGraphData, Util } from '@antv/g6'; // @ts-ignore @@ -31,12 +32,6 @@ export type Rect = { height: number; }; -export type TrainTask = { - ins_id: number; - name: string; - task_id: string; -}; - export interface TrainDataset extends NodeConfig { repo_id: number; name: string;