| @@ -55,7 +55,10 @@ function AutoMLInstance() { | |||
| // 这个接口返回的状态有延时,SSE 返回的状态是最新的 | |||
| // SSE 调用时,不需要解析 node_status, 也不要重新建立 SSE | |||
| if (isStatusDetermined) { | |||
| setInstanceInfo(info); | |||
| setInstanceInfo((prev) => ({ | |||
| ...info, | |||
| nodeStatus: prev!.nodeStatus, | |||
| })); | |||
| return; | |||
| } | |||
| @@ -101,7 +104,7 @@ function AutoMLInstance() { | |||
| ) as NodeStatus; | |||
| if (statusData) { | |||
| setInstanceInfo((prev) => ({ | |||
| ...(prev as AutoMLInstanceData), | |||
| ...prev!, | |||
| nodeStatus: statusData, | |||
| })); | |||
| @@ -172,7 +175,11 @@ function AutoMLInstance() { | |||
| label: '实验结果', | |||
| icon: <KFIcon type="icon-shiyanjieguo1" />, | |||
| children: ( | |||
| <ExperimentResult fileUrl={instanceInfo?.result_path} imageUrl={instanceInfo?.img_path} /> | |||
| <ExperimentResult | |||
| fileUrl={instanceInfo?.result_path} | |||
| imageUrl={instanceInfo?.img_path} | |||
| modelPath={instanceInfo?.model_path} | |||
| /> | |||
| ), | |||
| }, | |||
| { | |||
| @@ -6,9 +6,25 @@ | |||
| background-color: white; | |||
| border-radius: 10px; | |||
| &__download { | |||
| display: flex; | |||
| align-items: center; | |||
| height: 34px; | |||
| margin-bottom: 16px; | |||
| padding-left: @content-padding; | |||
| color: @text-color; | |||
| font-size: 13px; | |||
| background-color: #f8f8f9; | |||
| border-radius: 4px; | |||
| &__btn { | |||
| margin-left: 22px; | |||
| } | |||
| } | |||
| &__text { | |||
| width: 100%; | |||
| height: 460px; | |||
| height: 420px; | |||
| padding: 20px @content-padding; | |||
| overflow: auto; | |||
| white-space: pre-wrap; | |||
| @@ -20,14 +36,19 @@ | |||
| width: 100%; | |||
| overflow-x: auto; | |||
| :global { | |||
| .ant-image { | |||
| margin-right: 20px; | |||
| &:last-child { | |||
| margin-right: 0; | |||
| } | |||
| } | |||
| } | |||
| &__item { | |||
| height: 248px; | |||
| margin-right: 20px; | |||
| border: 1px solid rgba(96, 107, 122, 0.3); | |||
| &:last-child { | |||
| margin-right: 0; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,15 +1,18 @@ | |||
| import InfoGroup from '@/components/InfoGroup'; | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import { getFileReq } from '@/services/file'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Button, Image } from 'antd'; | |||
| import { useEffect, useMemo, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| type ExperimentResultProps = { | |||
| fileUrl?: string; | |||
| imageUrl?: string; | |||
| modelPath?: string; | |||
| }; | |||
| function ExperimentResult({ fileUrl, imageUrl }: ExperimentResultProps) { | |||
| function ExperimentResult({ fileUrl, imageUrl, modelPath }: ExperimentResultProps) { | |||
| const [result, setResult] = useState<string | undefined>(''); | |||
| const images = useMemo(() => { | |||
| @@ -35,20 +38,46 @@ function ExperimentResult({ fileUrl, imageUrl }: ExperimentResultProps) { | |||
| return ( | |||
| <div className={styles['experiment-result']}> | |||
| {modelPath && ( | |||
| <div className={styles['experiment-result__download']}> | |||
| <span style={{ marginRight: '12px', color: '#606b7a' }}>文件名</span> | |||
| <span>save_model.joblib</span> | |||
| <Button | |||
| type="link" | |||
| className={styles['experiment-result__download__btn']} | |||
| icon={<KFIcon type="icon-a-xiazai1" />} | |||
| size="small" | |||
| iconPosition="end" | |||
| onClick={() => { | |||
| window.location.href = modelPath; | |||
| }} | |||
| > | |||
| 模型下载 | |||
| </Button> | |||
| </div> | |||
| )} | |||
| <InfoGroup title="实验结果" contentScroll> | |||
| <div className={styles['experiment-result__text']}>{result}</div> | |||
| </InfoGroup> | |||
| <InfoGroup title="可视化结果" style={{ marginTop: '16px' }}> | |||
| <div className={styles['experiment-result__images']}> | |||
| {images.map((item, index) => ( | |||
| <img | |||
| key={index} | |||
| className={styles['experiment-result__images__item']} | |||
| src={item} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| ))} | |||
| <Image.PreviewGroup | |||
| preview={{ | |||
| onChange: (current, prev) => | |||
| console.log(`current index: ${current}, prev index: ${prev}`), | |||
| }} | |||
| > | |||
| {images.map((item) => ( | |||
| <Image | |||
| key={item} | |||
| className={styles['experiment-result__images__item']} | |||
| src={item} | |||
| height={248} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| ))} | |||
| </Image.PreviewGroup> | |||
| </div> | |||
| </InfoGroup> | |||
| </div> | |||
| @@ -52,7 +52,7 @@ function LogGroup({ | |||
| const [_isMouseDown, setIsMouseDown, isMouseDownRef] = useStateRef(false); | |||
| const preStatusRef = useRef<ExperimentStatus | undefined>(undefined); | |||
| const socketRef = useRef<WebSocket | undefined>(undefined); | |||
| const retryRef = useRef(2); // 等待 2 秒,重试 2 次 | |||
| const retryRef = useRef(2); // 等待 2 秒,重试 3 次 | |||
| useEffect(() => { | |||
| scrollToBottom(false); | |||
| @@ -147,7 +147,7 @@ function LogGroup({ | |||
| socket.addEventListener('close', (event) => { | |||
| console.log('WebSocket is closed:', event); | |||
| // 有时候会出现连接失败,重试 2 次 | |||
| // 有时候会出现连接失败,重试 3 次 | |||
| if (event.code !== 1000 && retryRef.current > 0) { | |||
| retryRef.current -= 1; | |||
| setTimeout(() => { | |||