| @@ -1,7 +1,7 @@ | |||
| import datasetImg from '@/assets/img/modal-select-dataset.png'; | |||
| import mirrorImg from '@/assets/img/modal-select-mirror.png'; | |||
| import modelImg from '@/assets/img/modal-select-model.png'; | |||
| import { AvailableRange, CommonTabKeys } from '@/enums'; | |||
| import { AvailableRange, CommonTabKeys, MirrorVersionStatus } from '@/enums'; | |||
| import { ResourceData, ResourceVersionData } from '@/pages/Dataset/config'; | |||
| import { MirrorVersionData } from '@/pages/Mirror/Info'; | |||
| import { MirrorData } from '@/pages/Mirror/List'; | |||
| @@ -224,8 +224,7 @@ export class MirrorSelector implements SelectorTypeInfo { | |||
| image_id: parentKey, | |||
| page: 0, | |||
| size: 2000, | |||
| status: 'available', | |||
| state: 1, | |||
| status: MirrorVersionStatus.Available, | |||
| }); | |||
| if (res && res.data) { | |||
| const list = res.data.content || []; | |||
| @@ -51,13 +51,12 @@ function ActiveLearnInstance() { | |||
| const [res] = await to(getActiveLearnInsReq(instanceId)); | |||
| if (res && res.data) { | |||
| const info = res.data as ActiveLearnInstanceData; | |||
| const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time } = info; | |||
| const { param, node_status, argo_ins_name, argo_ins_ns, status } = info; | |||
| // 解析配置参数 | |||
| const paramJson = parseJsonText(param); | |||
| if (paramJson) { | |||
| setExperimentInfo({ | |||
| ...paramJson.data, | |||
| create_time, | |||
| }); | |||
| } | |||
| @@ -69,7 +68,7 @@ function ActiveLearnInstance() { | |||
| return; | |||
| } | |||
| // 进行节点状态 | |||
| // 设置节点状态 | |||
| const nodeStatusJson = parseJsonText(node_status); | |||
| if (nodeStatusJson) { | |||
| setNodes(nodeStatusJson); | |||
| @@ -213,11 +213,7 @@ function BasicInfo({ | |||
| return ( | |||
| <div className={classNames(styles['active-learn-basic'], className)}> | |||
| {isInstance && runStatus && ( | |||
| <ExperimentRunBasic | |||
| create_time={info?.create_time} | |||
| runStatus={runStatus} | |||
| instanceStatus={instanceStatus} | |||
| /> | |||
| <ExperimentRunBasic runStatus={runStatus} instanceStatus={instanceStatus} /> | |||
| )} | |||
| {!isInstance && ( | |||
| <ConfigInfo | |||
| @@ -51,7 +51,7 @@ function AutoMLInstance() { | |||
| const [res] = await to(getExperimentInsReq(instanceId)); | |||
| if (res && res.data) { | |||
| const info = res.data as AutoMLInstanceData; | |||
| const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time, type } = info; | |||
| const { param, node_status, argo_ins_name, argo_ins_ns, status, type } = info; | |||
| setType(type); | |||
| // 解析配置参数 | |||
| @@ -59,7 +59,6 @@ function AutoMLInstance() { | |||
| if (paramJson) { | |||
| setAutoMLInfo({ | |||
| ...paramJson.data, | |||
| create_time, | |||
| type, | |||
| }); | |||
| } | |||
| @@ -40,6 +40,7 @@ type AutoMLBasicProps = { | |||
| isInstance?: boolean; | |||
| runStatus?: NodeStatus; | |||
| instanceStatus?: ExperimentStatus; | |||
| instanceCreateTime?: string; | |||
| }; | |||
| function AutoMLBasic({ | |||
| @@ -293,11 +294,7 @@ function AutoMLBasic({ | |||
| return ( | |||
| <div className={classNames(styles['auto-ml-basic'], className)}> | |||
| {isInstance && runStatus && ( | |||
| <ExperimentRunBasic | |||
| create_time={info?.create_time} | |||
| runStatus={runStatus} | |||
| instanceStatus={instanceStatus} | |||
| /> | |||
| <ExperimentRunBasic runStatus={runStatus} instanceStatus={instanceStatus} /> | |||
| )} | |||
| {!isInstance && ( | |||
| <ConfigInfo | |||
| @@ -182,14 +182,7 @@ function ExperimentInstanceList({ | |||
| > | |||
| {index + 1} | |||
| </a> | |||
| <ExperimentInstanceComponent | |||
| create_time={item.create_time} | |||
| finish_time={item.finish_time} | |||
| status={item.status as ExperimentStatus} | |||
| argo_ins_name={item.argo_ins_name} | |||
| argo_ins_ns={item.argo_ins_ns} | |||
| experimentInsId={item.id} | |||
| ></ExperimentInstanceComponent> | |||
| <ExperimentInstanceComponent instance={item}></ExperimentInstanceComponent> | |||
| <div className={styles.operation}> | |||
| <Button | |||
| type="link" | |||
| @@ -2,29 +2,25 @@ 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 { getWorkflowStatus } from '@/utils'; | |||
| import { ExperimentCompleted } from '@/utils/constant'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import { Typography } from 'antd'; | |||
| import React, { useCallback } from 'react'; | |||
| import styles from './index.less'; | |||
| type ExperimentInstanceProps = { | |||
| create_time?: string; | |||
| finish_time?: string; | |||
| status: ExperimentStatus; | |||
| argo_ins_name: string; | |||
| argo_ins_ns: string; | |||
| experimentInsId: number; | |||
| type ExperimentInstanceComponentProps = { | |||
| instance: ExperimentInstance; | |||
| }; | |||
| function ExperimentInstance({ | |||
| create_time, | |||
| finish_time, | |||
| status, | |||
| argo_ins_name, | |||
| argo_ins_ns, | |||
| experimentInsId, | |||
| }: ExperimentInstanceProps) { | |||
| function ExperimentInstanceComponent({ instance }: ExperimentInstanceComponentProps) { | |||
| const { id, argo_ins_name, argo_ins_ns, node_status, create_time, finish_time } = instance; | |||
| const status = instance.status as ExperimentStatus; | |||
| const workflowStatus = getWorkflowStatus(node_status) as NodeStatus | undefined; | |||
| const createTime = workflowStatus?.startedAt ?? create_time; | |||
| const finishTime = workflowStatus?.finishedAt ?? finish_time; | |||
| const handleSSEMessage: MessageHandler = useCallback( | |||
| (experimentInsId: number, status: string, finish_time: string) => { | |||
| window.postMessage({ | |||
| @@ -38,16 +34,16 @@ function ExperimentInstance({ | |||
| }, | |||
| [], | |||
| ); | |||
| useSSE(experimentInsId, status, argo_ins_name, argo_ins_ns, handleSSEMessage); | |||
| useSSE(id, status, argo_ins_name, argo_ins_ns, handleSSEMessage); | |||
| return ( | |||
| <React.Fragment> | |||
| <div className={styles.description}> | |||
| <RunDuration createTime={create_time} finishTime={finish_time} /> | |||
| <RunDuration createTime={createTime} finishTime={finishTime} /> | |||
| </div> | |||
| <div className={styles.startTime}> | |||
| <Typography.Text ellipsis={{ tooltip: formatDate(create_time) }}> | |||
| {formatDate(create_time)} | |||
| <Typography.Text ellipsis={{ tooltip: formatDate(createTime) }}> | |||
| {formatDate(createTime)} | |||
| </Typography.Text> | |||
| </div> | |||
| <div className={styles.statusBox}> | |||
| @@ -65,4 +61,4 @@ function ExperimentInstance({ | |||
| ); | |||
| } | |||
| export default ExperimentInstance; | |||
| export default ExperimentInstanceComponent; | |||
| @@ -39,7 +39,7 @@ export enum ExperimentListType { | |||
| } | |||
| type ExperimentListInfo = { | |||
| getListReq: (params: any) => Promise<any>; // 获取列表 | |||
| getListReq: (params: any, skipLoading?: boolean) => Promise<any>; // 获取列表 | |||
| getInsListReq: (params: any) => Promise<any>; // 获取实例列表 | |||
| deleteRecordReq: (params: any) => Promise<any>; // 删除 | |||
| runRecordReq: (params: any) => Promise<any>; // 运行 | |||
| @@ -63,20 +63,23 @@ function ExperimentList({ type }: ExperimentListProps) { | |||
| const timerRef = useRef<ReturnType<typeof window.setTimeout> | undefined>(); | |||
| // 获取自主机器学习或超参数自动优化列表 | |||
| const getAutoMLList = useCallback(async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| [config.nameProperty]: searchText || undefined, | |||
| }; | |||
| const request = config.getListReq; | |||
| const [res] = await to(request(params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }, [pagination, searchText, config]); | |||
| const getAutoMLList = useCallback( | |||
| async (skipLoading: boolean = false) => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| [config.nameProperty]: searchText || undefined, | |||
| }; | |||
| const request = config.getListReq; | |||
| const [res] = await to(request(params, skipLoading)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }, | |||
| [pagination, searchText, config], | |||
| ); | |||
| useEffect(() => { | |||
| getAutoMLList(); | |||
| @@ -111,9 +114,12 @@ function ExperimentList({ type }: ExperimentListProps) { | |||
| // 刷新实验列表状态, | |||
| // TODO: 目前是直接刷新实验列表,后续需要优化,只刷新状态 | |||
| const refreshExperimentList = useCallback(() => { | |||
| getAutoMLList(); | |||
| }, [getAutoMLList]); | |||
| const refreshExperimentList = useCallback( | |||
| (skipLoading: boolean = false) => { | |||
| getAutoMLList(skipLoading); | |||
| }, | |||
| [getAutoMLList], | |||
| ); | |||
| // 刷新实验实例列表 | |||
| const refreshExperimentIns = useCallback( | |||
| @@ -150,7 +156,7 @@ function ExperimentList({ type }: ExperimentListProps) { | |||
| } | |||
| timerRef.current = setTimeout(() => { | |||
| refreshExperimentList(); | |||
| refreshExperimentList(true); | |||
| }, 10000); | |||
| } | |||
| }; | |||
| @@ -8,12 +8,11 @@ import { Flex } from 'antd'; | |||
| import { useMemo } from 'react'; | |||
| type ExperimentRunBasicProps = { | |||
| create_time?: string; | |||
| runStatus?: NodeStatus; | |||
| instanceStatus?: ExperimentStatus; | |||
| }; | |||
| function ExperimentRunBasic({ create_time, runStatus, instanceStatus }: ExperimentRunBasicProps) { | |||
| function ExperimentRunBasic({ runStatus, instanceStatus }: ExperimentRunBasicProps) { | |||
| const instanceDatas = useMemo(() => { | |||
| if (!runStatus) { | |||
| return []; | |||
| @@ -26,11 +25,11 @@ function ExperimentRunBasic({ create_time, runStatus, instanceStatus }: Experime | |||
| return [ | |||
| { | |||
| label: '启动时间', | |||
| value: formatDate(create_time), | |||
| value: formatDate(runStatus.startedAt), | |||
| }, | |||
| { | |||
| label: '执行时长', | |||
| value: <RunDuration createTime={create_time} finishTime={runStatus.finishedAt} />, | |||
| value: <RunDuration createTime={runStatus.startedAt} finishTime={runStatus.finishedAt} />, | |||
| }, | |||
| { | |||
| label: '状态', | |||
| @@ -55,7 +54,7 @@ function ExperimentRunBasic({ create_time, runStatus, instanceStatus }: Experime | |||
| ), | |||
| }, | |||
| ]; | |||
| }, [runStatus, create_time, instanceStatus]); | |||
| }, [runStatus, instanceStatus]); | |||
| return ( | |||
| <ConfigInfo | |||
| @@ -197,14 +197,7 @@ function ExperimentInstanceList({ | |||
| )} | |||
| </div> | |||
| <ExperimentInstanceComponent | |||
| create_time={item.create_time} | |||
| finish_time={item.finish_time} | |||
| status={item.status as ExperimentStatus} | |||
| argo_ins_name={item.argo_ins_name} | |||
| argo_ins_ns={item.argo_ins_ns} | |||
| experimentInsId={item.id} | |||
| ></ExperimentInstanceComponent> | |||
| <ExperimentInstanceComponent instance={item}></ExperimentInstanceComponent> | |||
| <div className={styles.operation}> | |||
| <Button | |||
| @@ -2,29 +2,25 @@ 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 { getWorkflowStatus } from '@/utils'; | |||
| import { ExperimentCompleted } from '@/utils/constant'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import { Typography } from 'antd'; | |||
| import React, { useCallback } from 'react'; | |||
| import styles from './index.less'; | |||
| type ExperimentInstanceProps = { | |||
| create_time?: string; | |||
| finish_time?: string; | |||
| status: ExperimentStatus; | |||
| argo_ins_name: string; | |||
| argo_ins_ns: string; | |||
| experimentInsId: number; | |||
| type ExperimentInstanceComponentProps = { | |||
| instance: ExperimentInstance; | |||
| }; | |||
| function ExperimentInstance({ | |||
| create_time, | |||
| finish_time, | |||
| status, | |||
| argo_ins_name, | |||
| argo_ins_ns, | |||
| experimentInsId, | |||
| }: ExperimentInstanceProps) { | |||
| function ExperimentInstanceComponent({ instance }: ExperimentInstanceComponentProps) { | |||
| const { id, argo_ins_name, argo_ins_ns, nodes_status, create_time, finish_time } = instance; | |||
| const status = instance.status as ExperimentStatus; | |||
| const workflowStatus = getWorkflowStatus(nodes_status) as NodeStatus | undefined; | |||
| const createTime = workflowStatus?.startedAt ?? create_time; | |||
| const finishTime = workflowStatus?.finishedAt ?? finish_time; | |||
| const handleSSEMessage: MessageHandler = useCallback( | |||
| (experimentInsId: number, status: string, finish_time: string) => { | |||
| window.postMessage({ | |||
| @@ -38,17 +34,17 @@ function ExperimentInstance({ | |||
| }, | |||
| [], | |||
| ); | |||
| useSSE(experimentInsId, status, argo_ins_name, argo_ins_ns, handleSSEMessage); | |||
| useSSE(id, status, argo_ins_name, argo_ins_ns, handleSSEMessage); | |||
| return ( | |||
| <React.Fragment> | |||
| <div className={styles.description}> | |||
| <div style={{ width: '50%' }}> | |||
| <RunDuration createTime={create_time} finishTime={finish_time} /> | |||
| <RunDuration createTime={createTime} finishTime={finishTime} /> | |||
| </div> | |||
| <div style={{ width: '50%' }} className={styles.startTime}> | |||
| <Typography.Text ellipsis={{ tooltip: formatDate(create_time) }}> | |||
| {formatDate(create_time)} | |||
| <Typography.Text ellipsis={{ tooltip: formatDate(createTime) }}> | |||
| {formatDate(createTime)} | |||
| </Typography.Text> | |||
| </div> | |||
| </div> | |||
| @@ -67,4 +63,4 @@ function ExperimentInstance({ | |||
| ); | |||
| } | |||
| export default ExperimentInstance; | |||
| export default ExperimentInstanceComponent; | |||
| @@ -60,29 +60,35 @@ function Experiment() { | |||
| const timerRef = useRef(); | |||
| // 获取实验列表 | |||
| const getExperimentList = useCallback(async () => { | |||
| const params = { | |||
| page: pagination.current - 1, | |||
| size: pagination.pageSize, | |||
| name: searchText || undefined, | |||
| }; | |||
| const [res] = await to(getExperiment(params)); | |||
| if (res && res.data && Array.isArray(res.data.content)) { | |||
| setExperimentList( | |||
| res.data.content.map((item) => { | |||
| return { ...item, key: item.id }; | |||
| }), | |||
| ); | |||
| setTotal(res.data.totalElements); | |||
| } | |||
| }, [pagination, searchText]); | |||
| const getExperimentList = useCallback( | |||
| async (skipLoading) => { | |||
| const params = { | |||
| page: pagination.current - 1, | |||
| size: pagination.pageSize, | |||
| name: searchText || undefined, | |||
| }; | |||
| const [res] = await to(getExperiment(params, skipLoading)); | |||
| if (res && res.data && Array.isArray(res.data.content)) { | |||
| setExperimentList( | |||
| res.data.content.map((item) => { | |||
| return { ...item, key: item.id }; | |||
| }), | |||
| ); | |||
| setTotal(res.data.totalElements); | |||
| } | |||
| }, | |||
| [pagination, searchText], | |||
| ); | |||
| // 刷新实验列表状态, | |||
| // 目前是直接刷新实验列表,后续需要优化,只刷新状态 | |||
| const refreshExperimentList = useCallback(() => { | |||
| getExperimentList(); | |||
| }, [getExperimentList]); | |||
| const refreshExperimentList = useCallback( | |||
| (skipLoading) => { | |||
| getExperimentList(skipLoading); | |||
| }, | |||
| [getExperimentList], | |||
| ); | |||
| // 获取流水线列表 | |||
| useEffect(() => { | |||
| @@ -137,7 +143,7 @@ function Experiment() { | |||
| } | |||
| timerRef.current = setTimeout(() => { | |||
| refreshExperimentList(); | |||
| refreshExperimentList(true); | |||
| }, 10000); | |||
| } | |||
| }; | |||
| @@ -51,7 +51,7 @@ function HyperParameterInstance() { | |||
| const [res] = await to(getRayInsReq(instanceId)); | |||
| if (res && res.data) { | |||
| const info = res.data as HyperParameterInstanceData; | |||
| const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time } = info; | |||
| const { param, node_status, argo_ins_name, argo_ins_ns, status } = info; | |||
| // 解析配置参数 | |||
| const paramJson = parseJsonText(param).data; | |||
| if (paramJson) { | |||
| @@ -72,7 +72,6 @@ function HyperParameterInstance() { | |||
| } | |||
| setExperimentInfo({ | |||
| ...paramJson, | |||
| create_time, | |||
| }); | |||
| } | |||
| @@ -1,9 +1,7 @@ | |||
| import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | |||
| import RunDuration from '@/components/RunDuration'; | |||
| import { ExperimentStatus, hyperParameterOptimizedMode } from '@/enums'; | |||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||
| import ExperimentRunBasic from '@/pages/AutoML/components/ExperimentRunBasic'; | |||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||
| import { | |||
| schedulerAlgorithms, | |||
| searchAlgorithms, | |||
| @@ -18,7 +16,6 @@ import { | |||
| formatMirror, | |||
| formatModel, | |||
| } from '@/utils/format'; | |||
| import { Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useMemo } from 'react'; | |||
| import ParameterInfo from '../ParameterInfo'; | |||
| @@ -83,7 +80,7 @@ function HyperParameterBasic({ | |||
| } | |||
| return [ | |||
| { | |||
| label: '代码', | |||
| label: '代码配置', | |||
| value: info.code_config, | |||
| format: formatCodeConfig, | |||
| }, | |||
| @@ -145,56 +142,10 @@ function HyperParameterBasic({ | |||
| ]; | |||
| }, [info, getResourceDescription]); | |||
| const instanceDatas = useMemo(() => { | |||
| if (!info || !runStatus) { | |||
| return []; | |||
| } | |||
| return [ | |||
| { | |||
| label: '启动时间', | |||
| value: formatDate(info.create_time), | |||
| ellipsis: true, | |||
| }, | |||
| { | |||
| label: '执行时长', | |||
| value: <RunDuration createTime={info.create_time} finishTime={runStatus.finishedAt} />, | |||
| ellipsis: true, | |||
| }, | |||
| { | |||
| label: '状态', | |||
| value: ( | |||
| <Flex align="center"> | |||
| <img | |||
| style={{ width: '17px', marginRight: '7px' }} | |||
| src={experimentStatusInfo[runStatus.phase]?.icon} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| <div | |||
| style={{ | |||
| color: experimentStatusInfo[runStatus?.phase]?.color, | |||
| fontSize: '15px', | |||
| lineHeight: 1.6, | |||
| }} | |||
| > | |||
| {experimentStatusInfo[runStatus?.phase]?.label} | |||
| </div> | |||
| </Flex> | |||
| ), | |||
| ellipsis: true, | |||
| }, | |||
| ]; | |||
| }, [runStatus, info]); | |||
| return ( | |||
| <div className={classNames(styles['hyper-parameter-basic'], className)}> | |||
| {isInstance && runStatus && ( | |||
| <ExperimentRunBasic | |||
| create_time={info?.create_time} | |||
| runStatus={runStatus} | |||
| instanceStatus={instanceStatus} | |||
| /> | |||
| <ExperimentRunBasic runStatus={runStatus} instanceStatus={instanceStatus} /> | |||
| )} | |||
| {!isInstance && ( | |||
| <ConfigInfo | |||
| @@ -7,10 +7,11 @@ | |||
| import { request } from '@umijs/max'; | |||
| // 分页查询超参数自动寻优 | |||
| export function getActiveLearnListReq(params) { | |||
| export function getActiveLearnListReq(params, skipLoading) { | |||
| return request(`/api/mmp/activeLearn`, { | |||
| method: 'GET', | |||
| params, | |||
| skipLoading | |||
| }); | |||
| } | |||
| @@ -7,10 +7,11 @@ | |||
| import { request } from '@umijs/max'; | |||
| // 分页查询自动学习 | |||
| export function getAutoMLListReq(params) { | |||
| export function getAutoMLListReq(params,skipLoading) { | |||
| return request(`/api/mmp/machineLearn`, { | |||
| method: 'GET', | |||
| params, | |||
| skipLoading, | |||
| }); | |||
| } | |||
| @@ -1,9 +1,10 @@ | |||
| import { request } from '@umijs/max'; | |||
| // 查询实验列表 | |||
| export function getExperiment(params) { | |||
| export function getExperiment(params, skipLoading) { | |||
| return request(`/api/mmp/experiment`, { | |||
| method: 'GET', | |||
| params, | |||
| skipLoading, | |||
| }); | |||
| } | |||
| // 运行实验 | |||
| @@ -7,10 +7,11 @@ | |||
| import { request } from '@umijs/max'; | |||
| // 分页查询超参数自动寻优 | |||
| export function getRayListReq(params) { | |||
| export function getRayListReq(params, skipLoading) { | |||
| return request(`/api/mmp/ray`, { | |||
| method: 'GET', | |||
| params, | |||
| skipLoading | |||
| }); | |||
| } | |||
| @@ -52,6 +52,7 @@ export type ExperimentInstance = { | |||
| nodes_result: { | |||
| [key: string]: any; | |||
| }; | |||
| node_status: string; | |||
| nodes_status: string; | |||
| global_param: PipelineGlobalParam[]; | |||
| tensorBoardStatus?: TensorBoardStatus; | |||
| @@ -6,7 +6,6 @@ | |||
| import { PageEnum } from '@/enums/pagesEnums'; | |||
| import G6 from '@antv/g6'; | |||
| import { number } from 'echarts'; | |||
| /** | |||
| * 生成 8 位随机数 | |||
| @@ -279,8 +278,14 @@ export const getGitUrl = (url: string, branch: string): string => { | |||
| if (!url) { | |||
| return ''; | |||
| } | |||
| const gitUrl = url.replace(/\.git$/, ''); | |||
| return branch ? `${gitUrl}/tree/${branch}` : gitUrl; | |||
| let gitUrlStr = url.replace(/\.git$/, ''); | |||
| const gitUrl = new URL(gitUrlStr); | |||
| if (gitUrl.port === '30202') { | |||
| gitUrl.port = '30203'; // 30202 该为 30203 | |||
| } | |||
| gitUrlStr = gitUrl.href; | |||
| return branch ? `${gitUrlStr.toString()}/tree/${branch}` : gitUrlStr; | |||
| }; | |||
| /** | |||
| @@ -348,3 +353,26 @@ export const convertEmptyStringToUndefined = (value?: string): string | undefine | |||
| return value === '' ? undefined : value; | |||
| }; | |||
| /** | |||
| * 获取工作流节点 | |||
| * | |||
| * @param node_status - the status of the node | |||
| * @return the workflow node | |||
| */ | |||
| export const getWorkflowStatus = (node_status?: string | null) => { | |||
| if (!node_status) { | |||
| return; | |||
| } | |||
| const nodeStatusJson = parseJsonText(node_status); | |||
| if (!nodeStatusJson) { | |||
| return; | |||
| } | |||
| for (const key in nodeStatusJson) { | |||
| if (key.startsWith('workflow')) { | |||
| return nodeStatusJson[key]; | |||
| } | |||
| } | |||
| return; | |||
| }; | |||
| @@ -0,0 +1,19 @@ | |||
| import { getGitUrl } from '../src/utils'; | |||
| describe('canBeConvertToDate()', () => { | |||
| test('empty string', () => { | |||
| expect(getGitUrl('', '')).toBe(''); | |||
| }); | |||
| test('url domain with branch', () => { | |||
| expect( | |||
| getGitUrl('https://gitlink.org.cn/somunslotus/material-atom-predict.git', 'master'), | |||
| ).toBe('https://gitlink.org.cn/somunslotus/material-atom-predict/tree/master'); | |||
| }); | |||
| test('url domain without branch', () => { | |||
| expect(getGitUrl('https://gitlink.org.cn/somunslotus/material-atom-predict.git', '')).toBe( | |||
| 'https://gitlink.org.cn/somunslotus/material-atom-predict.git', | |||
| ); | |||
| }); | |||
| }); | |||