| @@ -21,6 +21,7 @@ const classificationAlgorithms = [ | |||||
| 'lda', | 'lda', | ||||
| 'liblinear_svc', | 'liblinear_svc', | ||||
| 'libsvm_svc', | 'libsvm_svc', | ||||
| 'tablenet', | |||||
| 'mlp', | 'mlp', | ||||
| 'multinomial_nb', | 'multinomial_nb', | ||||
| 'passive_aggressive', | 'passive_aggressive', | ||||
| @@ -30,13 +31,7 @@ const classificationAlgorithms = [ | |||||
| 'LightGBMClassification', | 'LightGBMClassification', | ||||
| 'XGBoostClassification', | 'XGBoostClassification', | ||||
| 'StackingClassification', | 'StackingClassification', | ||||
| ].map((name) => { | |||||
| if (name === 'mlp') { | |||||
| return { label: 'tablenet', value: name }; | |||||
| } else { | |||||
| return { label: name, value: name }; | |||||
| } | |||||
| }); | |||||
| ].map((name) => ({ label: name, value: name })); | |||||
| // 回归算法 | // 回归算法 | ||||
| const regressorAlgorithms = [ | const regressorAlgorithms = [ | ||||
| @@ -159,12 +159,13 @@ function ExperimentText() { | |||||
| return; | return; | ||||
| } | } | ||||
| const { finishedAt, phase, nodes = {} } = statusData; | const { finishedAt, phase, nodes = {} } = statusData; | ||||
| // 更新实验实例状态和结束时间 | // 更新实验实例状态和结束时间 | ||||
| setExperimentIns((prev) => ({ | |||||
| ...prev, | |||||
| finish_time: finishedAt, | |||||
| status: phase, | |||||
| })); | |||||
| // setExperimentIns((prev) => ({ | |||||
| // ...prev, | |||||
| // finish_time: finishedAt, | |||||
| // status: phase, | |||||
| // })); | |||||
| // 设置总 workflow 状态 | // 设置总 workflow 状态 | ||||
| const tempWorkflowStatus = Object.values(nodes).find((node) => | const tempWorkflowStatus = Object.values(nodes).find((node) => | ||||
| @@ -238,7 +238,7 @@ function CreateServiceVersion() { | |||||
| }, | }, | ||||
| { | { | ||||
| pattern: /^[a-zA-Z0-9._-]+$/, | pattern: /^[a-zA-Z0-9._-]+$/, | ||||
| message: '版本只支持字母、数字、点(.)、下划线(_)、中横线(-)', | |||||
| message: '服务支持字母、数字、点(.)、下划线(_)、中横线(-)', | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| > | > | ||||
| @@ -1,10 +1,9 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | |||||
| import { ExperimentStatus, experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import { ExperimentInstance } from '@/types'; | import { ExperimentInstance } from '@/types'; | ||||
| import { elapsedTime, formatDate } from '@/utils/date'; | |||||
| import { useNavigate } from '@umijs/max'; | import { useNavigate } from '@umijs/max'; | ||||
| import { Button, Empty } from 'antd'; | |||||
| import { Empty } from 'antd'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import ExperimentInstanceComponent from './instance'; | |||||
| type ExperimentTableProps = { | type ExperimentTableProps = { | ||||
| tableData: ExperimentInstance[]; | tableData: ExperimentInstance[]; | ||||
| style?: React.CSSProperties; | style?: React.CSSProperties; | ||||
| @@ -26,31 +25,11 @@ function ExperimentTable({ tableData = [], style }: ExperimentTableProps) { | |||||
| </div> | </div> | ||||
| {Array.isArray(tableData) && tableData.length > 0 ? ( | {Array.isArray(tableData) && tableData.length > 0 ? ( | ||||
| tableData.map((item) => ( | tableData.map((item) => ( | ||||
| <div className={styles['experiment-table__content']} key={item.id}> | |||||
| <div className={styles['experiment-table__status']} style={{ paddingLeft: '6.5px' }}> | |||||
| <img | |||||
| src={experimentStatusInfo[item.status as ExperimentStatus]?.icon} | |||||
| width={17} | |||||
| height={17} | |||||
| draggable={false} | |||||
| alt="" | |||||
| /> | |||||
| </div> | |||||
| <div className={styles['experiment-table__duration']}> | |||||
| {elapsedTime(item.create_time, item.finish_time)} | |||||
| </div> | |||||
| <div className={styles['experiment-table__date']}>{formatDate(item.create_time)}</div> | |||||
| <div className={styles['experiment-table__operation']}> | |||||
| <Button | |||||
| size="small" | |||||
| type="link" | |||||
| icon={<KFIcon type="icon-xiangqing2" font={14} />} | |||||
| onClick={() => gotoExperiment(item)} | |||||
| > | |||||
| 详情 | |||||
| </Button> | |||||
| </div> | |||||
| </div> | |||||
| <ExperimentInstanceComponent | |||||
| instance={item} | |||||
| key={item.id} | |||||
| onClick={() => gotoExperiment(item)} | |||||
| /> | |||||
| )) | )) | ||||
| ) : ( | ) : ( | ||||
| <Empty | <Empty | ||||
| @@ -0,0 +1,76 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | |||||
| import RunDuration from '@/components/RunDuration'; | |||||
| import { ExperimentStatus } from '@/enums'; | |||||
| import { useSSE, type MessageHandler } from '@/hooks/useSSE'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||||
| import { ExperimentInstance, NodeStatus } from '@/types'; | |||||
| import { formatDate } from '@/utils/date'; | |||||
| import { getExperimentInstanceStatus, getWorkflowStatus } from '@/utils/experiment'; | |||||
| import { Button } from 'antd'; | |||||
| import { useCallback, useState } from 'react'; | |||||
| import styles from './index.less'; | |||||
| const NodePrefix = 'workflow'; | |||||
| type ExperimentInstanceComponentProps = { | |||||
| instance: ExperimentInstance; | |||||
| onClick: () => void; | |||||
| }; | |||||
| function ExperimentInstanceComponent({ instance, onClick }: ExperimentInstanceComponentProps) { | |||||
| const { experiment_id, id, argo_ins_name, argo_ins_ns, nodes_status } = instance; | |||||
| const initialWorkflowStatus = getWorkflowStatus(nodes_status) as NodeStatus | undefined; | |||||
| const [workflowStatus, setWorkflowStatus] = useState<NodeStatus | undefined>( | |||||
| initialWorkflowStatus, | |||||
| ); | |||||
| const status = getExperimentInstanceStatus(instance.status as ExperimentStatus, workflowStatus); | |||||
| const createTime = workflowStatus?.startedAt; | |||||
| const finishTime = workflowStatus?.finishedAt; | |||||
| const statusInfo = experimentStatusInfo[status]; | |||||
| const handleSSEMessage: MessageHandler = useCallback( | |||||
| ( | |||||
| _experimentId: number, | |||||
| _experimentInsId: number, | |||||
| _status: string, | |||||
| _finishTime: string, | |||||
| nodes, | |||||
| ) => { | |||||
| if (nodes) { | |||||
| // 设置总 workflow 状态 | |||||
| const workflowStatus = Object.values(nodes).find((node: any) => | |||||
| node.displayName.startsWith(NodePrefix), | |||||
| ) as NodeStatus; | |||||
| if (workflowStatus) { | |||||
| setWorkflowStatus(workflowStatus); | |||||
| } | |||||
| } | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| useSSE(experiment_id, id, status, argo_ins_name, argo_ins_ns, handleSSEMessage); | |||||
| return ( | |||||
| <div className={styles['experiment-table__content']} key={id}> | |||||
| <div className={styles['experiment-table__status']} style={{ paddingLeft: '6.5px' }}> | |||||
| <img src={statusInfo?.icon} width={17} height={17} draggable={false} alt="" /> | |||||
| </div> | |||||
| <div className={styles['experiment-table__duration']}> | |||||
| <RunDuration createTime={createTime} finishTime={finishTime} /> | |||||
| </div> | |||||
| <div className={styles['experiment-table__date']}>{formatDate(createTime)}</div> | |||||
| <div className={styles['experiment-table__operation']}> | |||||
| <Button | |||||
| size="small" | |||||
| type="link" | |||||
| icon={<KFIcon type="icon-xiangqing2" font={14} />} | |||||
| onClick={onClick} | |||||
| > | |||||
| 详情 | |||||
| </Button> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ExperimentInstanceComponent; | |||||