| @@ -5,8 +5,9 @@ import { NodeStatus } from '@/types'; | |||
| export type MessageHandler = (experimentInsId: number, status: string, finishedAt: string, nodes: Record<string, NodeStatus>) => void | |||
| export const useSSE = (experimentInsId: number, status: ExperimentStatus, name: string, namespace: string, onMessage: MessageHandler) => { | |||
| const isRunning = status === ExperimentStatus.Pending || status === ExperimentStatus.Running | |||
| useEffect(() => { | |||
| if (status === ExperimentStatus.Pending || status === ExperimentStatus.Running) { | |||
| if (isRunning) { | |||
| const { origin } = location; | |||
| const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | |||
| const evtSource = new EventSource( | |||
| @@ -35,5 +36,5 @@ export const useSSE = (experimentInsId: number, status: ExperimentStatus, name: | |||
| } | |||
| } | |||
| }, [experimentInsId, status, name, namespace, onMessage]); | |||
| }, [experimentInsId, isRunning, name, namespace, onMessage]); | |||
| }; | |||
| @@ -11,44 +11,43 @@ import { useCallback, useEffect, useState } from 'react'; | |||
| let globalTimeOffset: number | undefined = undefined; | |||
| export const globalGetSeverTime = async () => { | |||
| const requestStartTime = Date.now() | |||
| const requestStartTime = Date.now(); | |||
| const [res] = await to(getSeverTimeReq()); | |||
| const requestEndTime = Date.now() | |||
| const requestEndTime = Date.now(); | |||
| const requestDuration = (requestEndTime - requestStartTime) / 2; | |||
| if (res && res.data) { | |||
| const serverDate = new Date(res.data); | |||
| const timeOffset = serverDate.getTime() + requestDuration - requestEndTime ; | |||
| const timeOffset = serverDate.getTime() + requestDuration - requestEndTime; | |||
| globalTimeOffset = timeOffset; | |||
| return timeOffset | |||
| return timeOffset; | |||
| } | |||
| }; | |||
| export const now = () => { | |||
| return new Date(Date.now() + (globalTimeOffset ?? 0)) | |||
| } | |||
| return new Date(Date.now() + (globalTimeOffset ?? 0)); | |||
| }; | |||
| /** 获取服务器时间 */ | |||
| export function useServerTime() { | |||
| const [timeOffset, setTimeOffset] = useState<number>(globalTimeOffset ?? 0); | |||
| useEffect(() => { | |||
| // 获取服务器时间 | |||
| const getSeverTime = async () => { | |||
| const [res] = await to(globalGetSeverTime()); | |||
| if (res) { | |||
| setTimeOffset(res) | |||
| setTimeOffset(res); | |||
| } | |||
| }; | |||
| // 获取服务器时间,防止第一次加载时,请求失败 | |||
| if (!globalTimeOffset) { | |||
| getSeverTime(); | |||
| } | |||
| }, []); | |||
| const now = useCallback(() => { | |||
| return new Date(Date.now() + timeOffset) | |||
| }, [timeOffset]) | |||
| return new Date(Date.now() + timeOffset); | |||
| }, [timeOffset]); | |||
| return [now, timeOffset] as const; | |||
| return [now] as const; | |||
| } | |||
| @@ -1,3 +1,5 @@ | |||
| @cellWidth: calc(100% + 32px + 33px - 48px - 200px - 344px); | |||
| .tableExpandBox { | |||
| display: flex; | |||
| align-items: center; | |||
| @@ -11,22 +13,22 @@ | |||
| } | |||
| .check { | |||
| width: calc((100% + 32px + 33px) / 5 / 2); | |||
| width: calc(@cellWidth * 3 / 20); // 15% | |||
| } | |||
| .index { | |||
| width: calc((100% + 32px + 33px) / 5 / 2); | |||
| width: calc(@cellWidth * 3 / 20); // 15% | |||
| } | |||
| .description { | |||
| display: flex; | |||
| flex: 1; | |||
| align-items: center; | |||
| width: calc(@cellWidth / 2); // 50% | |||
| } | |||
| .startTime { | |||
| .singleLine(); | |||
| width: 200px; | |||
| width: calc(@cellWidth / 5); // 20% | |||
| } | |||
| .status { | |||
| @@ -302,7 +302,7 @@ function ExperimentList({ type }: ExperimentListProps) { | |||
| title: '类型', | |||
| dataIndex: 'type', | |||
| key: 'type', | |||
| width: '10%', | |||
| width: '15%', | |||
| render: tableCellRender(false, TableCellValueType.Enum, { | |||
| options: autoMLTypeOptions, | |||
| }), | |||
| @@ -316,7 +316,7 @@ function ExperimentList({ type }: ExperimentListProps) { | |||
| title: '实验名称', | |||
| dataIndex: config.nameProperty, | |||
| key: 'name', | |||
| width: '20%', | |||
| width: '30%', | |||
| render: tableCellRender(false, TableCellValueType.Link, { | |||
| onClick: gotoDetail, | |||
| }), | |||
| @@ -326,13 +326,14 @@ function ExperimentList({ type }: ExperimentListProps) { | |||
| dataIndex: config.descProperty, | |||
| key: 'description', | |||
| render: tableCellRender(true), | |||
| width: type === ExperimentListType.AutoML ? '35%' : '50%', | |||
| }, | |||
| ...diffColumns, | |||
| { | |||
| title: '创建时间', | |||
| dataIndex: 'update_time', | |||
| key: 'update_time', | |||
| width: 200, | |||
| width: '20%', | |||
| render: tableCellRender(false, TableCellValueType.Date), | |||
| }, | |||
| { | |||
| @@ -58,5 +58,6 @@ | |||
| align-items: center; | |||
| color: #808080; | |||
| font-size: 13px; | |||
| min-width: 0; | |||
| } | |||
| } | |||
| @@ -14,6 +14,10 @@ type ResourceItemProps = { | |||
| }; | |||
| function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) { | |||
| const timeAgo = | |||
| '最近更新: ' + | |||
| (item.update_time ? formatDate(item.update_time, 'YYYY-MM-DD') : item.time_ago ?? ''); | |||
| const create_by = item.create_by ?? ''; | |||
| return ( | |||
| <div className={styles['resource-item']} onClick={() => onClick(item)}> | |||
| <Flex justify="space-between" align="center" style={{ marginBottom: '20px', height: '32px' }}> | |||
| @@ -37,7 +41,7 @@ function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) | |||
| )} | |||
| </Flex> | |||
| <div className={styles['resource-item__description']}>{item.description}</div> | |||
| <Flex justify="space-between"> | |||
| <Flex justify="space-between" gap={'0 8px'}> | |||
| <div className={styles['resource-item__time']}> | |||
| <img | |||
| style={{ width: '17px', marginRight: '6px' }} | |||
| @@ -45,14 +49,11 @@ function ResourceItem({ item, isPublic, onClick, onRemove }: ResourceItemProps) | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| <span>{item.create_by ?? ''}</span> | |||
| <Typography.Text ellipsis={{ tooltip: create_by }}>{create_by}</Typography.Text> | |||
| </div> | |||
| <div className={styles['resource-item__time']}> | |||
| <img style={{ width: '12px', marginRight: '5px' }} src={clock} draggable={false} alt="" /> | |||
| <span> | |||
| {'最近更新: '} | |||
| {item.update_time ? formatDate(item.update_time, 'YYYY-MM-DD') : item.time_ago ?? ''} | |||
| </span> | |||
| <Typography.Text ellipsis={{ tooltip: timeAgo }}>{timeAgo}</Typography.Text> | |||
| </div> | |||
| </Flex> | |||
| </div> | |||
| @@ -1,3 +1,4 @@ | |||
| import RunDuration from '@/components/RunDuration'; | |||
| import { ExperimentStatus } from '@/enums'; | |||
| import { useStateRef } from '@/hooks/useStateRef'; | |||
| import { useVisible } from '@/hooks/useVisible'; | |||
| @@ -5,7 +6,7 @@ import { getExperimentIns } from '@/services/experiment/index.js'; | |||
| import { getWorkflowById } from '@/services/pipeline/index.js'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { fittingString, parseJsonText } from '@/utils'; | |||
| import { elapsedTime, formatDate } from '@/utils/date'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import { to } from '@/utils/promise'; | |||
| import G6, { Util } from '@antv/g6'; | |||
| import { Button } from 'antd'; | |||
| @@ -15,8 +16,6 @@ import ExperimentDrawer from '../components/ExperimentDrawer'; | |||
| import ParamsModal from '../components/ViewParamsModal'; | |||
| import { experimentStatusInfo } from '../status'; | |||
| import styles from './index.less'; | |||
| import { useServerTime } from '@/hooks/useServerTime'; | |||
| import RunDuration from '@/components/RunDuration'; | |||
| let graph = null; | |||
| @@ -476,7 +475,10 @@ function ExperimentText() { | |||
| </div> | |||
| <div className={styles['pipeline-container__top__info']}> | |||
| 执行时长: | |||
| <RunDuration createTime={experimentIns?.create_time} finishTime={experimentIns?.finish_time} /> | |||
| <RunDuration | |||
| createTime={experimentIns?.create_time} | |||
| finishTime={experimentIns?.finish_time} | |||
| /> | |||
| </div> | |||
| <div className={styles['pipeline-container__top__info']}> | |||
| 状态: | |||
| @@ -36,6 +36,13 @@ | |||
| .ant-btn-variant-text { | |||
| color: #565658; | |||
| } | |||
| .anticon-question-circle { | |||
| margin-top: -12px; | |||
| margin-left: 1px !important; | |||
| color: @text-color-tertiary !important; | |||
| font-size: 12px !important; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -337,7 +337,11 @@ function CreateServiceVersion() { | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item label="代码配置" name="code_config"> | |||
| <Form.Item | |||
| label="代码配置" | |||
| name="code_config" | |||
| tooltip="此处代码配置为可引用的代码配置,具体代码编写指南见用户手册中服务部署模块" | |||
| > | |||
| <CodeSelect | |||
| placeholder="请选择代码配置" | |||
| canInput={false} | |||