| @@ -62,3 +62,4 @@ mvnw | |||||
| *storybook.log | *storybook.log | ||||
| /react-ui/docs | /react-ui/docs | ||||
| /react-ui/types/tsconfig.tsbuildinfo | |||||
| @@ -10,7 +10,7 @@ import '../public/fonts/font.css'; | |||||
| import { getAccessToken } from './access'; | import { getAccessToken } from './access'; | ||||
| import ErrorBoundary from './components/ErrorBoundary'; | import ErrorBoundary from './components/ErrorBoundary'; | ||||
| import './dayjsConfig'; | import './dayjsConfig'; | ||||
| import { removeAllPageCacheState } from './hooks/pageCacheState'; | |||||
| import { removeAllPageCacheState } from './hooks/useCacheState'; | |||||
| import { | import { | ||||
| getRemoteMenu, | getRemoteMenu, | ||||
| getRoutersInfo, | getRoutersInfo, | ||||
| @@ -1,4 +1,4 @@ | |||||
| import { filterResourceStandard, resourceFieldNames } from '@/hooks/resource'; | |||||
| import { filterResourceStandard, resourceFieldNames } from '@/hooks/useComputingResource'; | |||||
| import { ServiceData } from '@/pages/ModelDeployment/types'; | import { ServiceData } from '@/pages/ModelDeployment/types'; | ||||
| import { getDatasetList, getModelList } from '@/services/dataset/index.js'; | import { getDatasetList, getModelList } from '@/services/dataset/index.js'; | ||||
| import { getServiceListReq } from '@/services/modelDeployment'; | import { getServiceListReq } from '@/services/modelDeployment'; | ||||
| @@ -4,7 +4,7 @@ | |||||
| * @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务 | * @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务 | ||||
| */ | */ | ||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { Select, type SelectProps } from 'antd'; | import { Select, type SelectProps } from 'antd'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| @@ -16,13 +16,17 @@ export type ParameterSelectObject = { | |||||
| [key: string]: any; | [key: string]: any; | ||||
| }; | }; | ||||
| export type ParameterSelectDataType = 'dataset' | 'model' | 'service' | 'resource'; | |||||
| export interface ParameterSelectProps extends SelectProps { | export interface ParameterSelectProps extends SelectProps { | ||||
| /** 类型 */ | /** 类型 */ | ||||
| dataType: 'dataset' | 'model' | 'service' | 'resource'; | |||||
| dataType: ParameterSelectDataType; | |||||
| /** 是否只是展示信息 */ | /** 是否只是展示信息 */ | ||||
| display?: boolean; | display?: boolean; | ||||
| /** 值,支持对象,对象必须包含 value */ | /** 值,支持对象,对象必须包含 value */ | ||||
| value?: string | ParameterSelectObject; | value?: string | ParameterSelectObject; | ||||
| /** 用于流水线, 流水线资源规格要求 id 为字符串 */ | |||||
| isPipeline?: boolean; | |||||
| /** 修改后回调 */ | /** 修改后回调 */ | ||||
| onChange?: (value: string | ParameterSelectObject) => void; | onChange?: (value: string | ParameterSelectObject) => void; | ||||
| } | } | ||||
| @@ -32,6 +36,7 @@ function ParameterSelect({ | |||||
| dataType, | dataType, | ||||
| display = false, | display = false, | ||||
| value, | value, | ||||
| isPipeline = false, | |||||
| onChange, | onChange, | ||||
| ...rest | ...rest | ||||
| }: ParameterSelectProps) { | }: ParameterSelectProps) { | ||||
| @@ -39,6 +44,12 @@ function ParameterSelect({ | |||||
| const propsConfig = paramSelectConfig[dataType]; | const propsConfig = paramSelectConfig[dataType]; | ||||
| const valueText = typeof value === 'object' && value !== null ? value.value : value; | const valueText = typeof value === 'object' && value !== null ? value.value : value; | ||||
| const [resourceStandardList] = useComputingResource(); | const [resourceStandardList] = useComputingResource(); | ||||
| const computingResource = isPipeline | |||||
| ? resourceStandardList.map((v) => ({ | |||||
| ...v, | |||||
| id: String(v.id), | |||||
| })) | |||||
| : resourceStandardList; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| // 获取下拉数据 | // 获取下拉数据 | ||||
| @@ -56,7 +67,7 @@ function ParameterSelect({ | |||||
| getSelectOptions(); | getSelectOptions(); | ||||
| }, [propsConfig]); | }, [propsConfig]); | ||||
| const selectOptions = dataType === 'resource' ? resourceStandardList : options; | |||||
| const selectOptions = dataType === 'resource' ? computingResource : options; | |||||
| const handleChange = (text: string) => { | const handleChange = (text: string) => { | ||||
| if (typeof value === 'object' && value !== null) { | if (typeof value === 'object' && value !== null) { | ||||
| @@ -1,202 +0,0 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-15 10:01:29 | |||||
| * @Description: 自定义 hooks | |||||
| */ | |||||
| import { FormInstance } from 'antd'; | |||||
| import { debounce } from 'lodash'; | |||||
| import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; | |||||
| /** | |||||
| * 生成具有初始值的状态引用 | |||||
| * | |||||
| * @param initialValue - 状态的初始值 | |||||
| * @return 包含状态值、状态设置函数和可变引用对象的数组 | |||||
| */ | |||||
| export function useStateRef<T>(initialValue: T) { | |||||
| const [value, setValue] = useState(initialValue); | |||||
| const ref = useRef(value); | |||||
| useEffect(() => { | |||||
| ref.current = value; | |||||
| }, [value]); | |||||
| return [value, setValue, ref] as const; | |||||
| } | |||||
| /** | |||||
| * 生成一个自定义钩子,用于管理模态框的可见性状态。 | |||||
| * | |||||
| * @param initialValue - 模态框的初始可见性状态。 | |||||
| * @return 一个数组,包含可见性状态和打开和关闭模态框的函数。 | |||||
| */ | |||||
| export function useVisible(initialValue: boolean) { | |||||
| const [visible, setVisible] = useState(initialValue); | |||||
| const ref = useRef(initialValue); | |||||
| const open = useCallback(() => { | |||||
| setVisible(true); | |||||
| }, []); | |||||
| const close = useCallback(() => { | |||||
| setVisible(false); | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| ref.current = visible; | |||||
| }, [visible]); | |||||
| return [visible, open, close, ref] as const; | |||||
| } | |||||
| type Callback<T> = (state: T) => void; | |||||
| /** | |||||
| * 生成一个具有回调机制的可变状态值和更新它的函数。 | |||||
| * | |||||
| * @param initialValue - 初始状态值。 | |||||
| * @return 一个元组,包含当前状态值和用于更新状态的函数。 | |||||
| */ | |||||
| export function useCallbackState<T>(initialValue: T) { | |||||
| const [state, _setState] = useState<T>(initialValue); | |||||
| const callbackQueue = useRef<Callback<T>[]>([]); | |||||
| useEffect(() => { | |||||
| callbackQueue.current.forEach((cb) => cb(state)); | |||||
| callbackQueue.current = []; | |||||
| }, [state]); | |||||
| const setState = (newValue: T | ((prevState: T) => T), callback?: Callback<T>) => { | |||||
| _setState(newValue); | |||||
| if (callback && typeof callback === 'function') { | |||||
| callbackQueue.current.push(callback); | |||||
| } | |||||
| }; | |||||
| return [state, setState] as const; | |||||
| } | |||||
| /** | |||||
| * 用于追踪 DOM 元素尺寸的 hook。 | |||||
| * | |||||
| * @param initialWidth - 初始宽度。 | |||||
| * @param initialHeight - 初始高度。 | |||||
| * @param deps - 依赖列表。 | |||||
| * @return 一个元组,包含 DOM 元素的 ref、当前宽度和当前高度。 | |||||
| */ | |||||
| export function useDomSize<T extends HTMLElement>( | |||||
| initialWidth: number, | |||||
| initialHeight: number, | |||||
| deps: React.DependencyList = [], | |||||
| ) { | |||||
| const domRef = useRef<T>(null); | |||||
| const [width, setWidth] = useState(initialWidth); | |||||
| const [height, setHeight] = useState(initialHeight); | |||||
| useEffect(() => { | |||||
| const setDomHeight = () => { | |||||
| if (domRef.current) { | |||||
| setHeight(domRef.current.offsetHeight); | |||||
| setWidth(domRef.current.offsetWidth); | |||||
| } | |||||
| }; | |||||
| const debounceFunc = debounce(setDomHeight, 100); | |||||
| setDomHeight(); | |||||
| window.addEventListener('resize', debounceFunc); | |||||
| return () => { | |||||
| window.removeEventListener('resize', debounceFunc); | |||||
| }; | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, [domRef, ...deps]); | |||||
| return [domRef, { width, height }] as const; | |||||
| } | |||||
| /** | |||||
| * 用于在 modal 关闭时重置 Form 表单的 hook。 | |||||
| * | |||||
| * @param form - Ant Design Form 表单实例 | |||||
| * @param open - modal 是否打开 | |||||
| */ | |||||
| export const useResetFormOnCloseModal = (form: FormInstance, open: boolean) => { | |||||
| const prevOpenRef = useRef<boolean>(); | |||||
| useEffect(() => { | |||||
| prevOpenRef.current = open; | |||||
| }, [open]); | |||||
| const prevOpen = prevOpenRef.current; | |||||
| useEffect(() => { | |||||
| if (!open && prevOpen) { | |||||
| form.resetFields(); | |||||
| } | |||||
| }, [form, prevOpen, open]); | |||||
| }; | |||||
| /** | |||||
| * Executes the effect function when the specified condition is true. | |||||
| * | |||||
| * @param effect - The effect function to execute. | |||||
| * @param when - The condition to trigger the effect. | |||||
| * @param deps - The dependencies for the effect. | |||||
| */ | |||||
| export const useEffectWhen = (effect: () => void, when: boolean, deps: React.DependencyList) => { | |||||
| const requestFns = useRef<(() => void)[]>([]); | |||||
| useEffect(() => { | |||||
| if (when) { | |||||
| effect(); | |||||
| } else { | |||||
| requestFns.current.splice(0, 1, effect); | |||||
| } | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, deps); | |||||
| useEffect(() => { | |||||
| if (when) { | |||||
| const fn = requestFns.current.pop(); | |||||
| fn?.(); | |||||
| } | |||||
| }, [when]); | |||||
| }; | |||||
| // 选择、全选操作 | |||||
| export const useCheck = <T>(list: T[]) => { | |||||
| const [selected, setSelected] = useState<T[]>([]); | |||||
| const checked = useMemo(() => { | |||||
| return selected.length === list.length && selected.length > 0; | |||||
| }, [selected, list]); | |||||
| const indeterminate = useMemo(() => { | |||||
| return selected.length > 0 && selected.length < list.length; | |||||
| }, [selected, list]); | |||||
| const checkAll = useCallback(() => { | |||||
| setSelected(checked ? [] : list); | |||||
| }, [list, checked]); | |||||
| const isSingleChecked = useCallback((item: T) => selected.includes(item), [selected]); | |||||
| const checkSingle = useCallback( | |||||
| (item: T) => { | |||||
| setSelected((prev) => { | |||||
| if (isSingleChecked(item)) { | |||||
| return prev.filter((i) => i !== item); | |||||
| } else { | |||||
| return [...prev, item]; | |||||
| } | |||||
| }); | |||||
| }, | |||||
| [isSingleChecked], | |||||
| ); | |||||
| return [ | |||||
| selected, | |||||
| setSelected, | |||||
| checked, | |||||
| indeterminate, | |||||
| checkAll, | |||||
| isSingleChecked, | |||||
| checkSingle, | |||||
| ] as const; | |||||
| }; | |||||
| @@ -0,0 +1,25 @@ | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| type Callback<T> = (state: T) => void; | |||||
| /** | |||||
| * 生成一个具有回调机制的可变状态值和更新它的函数。谨慎使用 | |||||
| * | |||||
| * @param initialValue - 初始状态值。 | |||||
| * @return 一个元组,包含当前状态值和用于更新状态的函数。 | |||||
| */ | |||||
| export function useCallbackState<T>(initialValue: T) { | |||||
| const [state, _setState] = useState<T>(initialValue); | |||||
| const callbackQueue = useRef<Callback<T>[]>([]); | |||||
| useEffect(() => { | |||||
| callbackQueue.current.forEach((cb) => cb(state)); | |||||
| callbackQueue.current = []; | |||||
| }, [state]); | |||||
| const setState = (newValue: T | ((prevState: T) => T), callback?: Callback<T>) => { | |||||
| _setState(newValue); | |||||
| if (callback && typeof callback === 'function') { | |||||
| callbackQueue.current.push(callback); | |||||
| } | |||||
| }; | |||||
| return [state, setState] as const; | |||||
| } | |||||
| @@ -0,0 +1,53 @@ | |||||
| import { useCallback, useMemo, useState } from 'react'; | |||||
| /** | |||||
| * @description 选择、全选操作 | |||||
| * @param list 需要进行选择的列表 | |||||
| * @returns selected 选中的项 | |||||
| * setSelected 设置 selected 的方法 | |||||
| * checked 是否全选 | |||||
| * indeterminate 是否部分选中 | |||||
| * checkAll 全选 | |||||
| * isSingleChecked 是否单个选中 | |||||
| * checkSingle 单个选中 | |||||
| */ | |||||
| export const useCheck = <T>(list: T[]) => { | |||||
| const [selected, setSelected] = useState<T[]>([]); | |||||
| const checked = useMemo(() => { | |||||
| return selected.length === list.length && selected.length > 0; | |||||
| }, [selected, list]); | |||||
| const indeterminate = useMemo(() => { | |||||
| return selected.length > 0 && selected.length < list.length; | |||||
| }, [selected, list]); | |||||
| const checkAll = useCallback(() => { | |||||
| setSelected(checked ? [] : list); | |||||
| }, [list, checked]); | |||||
| const isSingleChecked = useCallback((item: T) => selected.includes(item), [selected]); | |||||
| const checkSingle = useCallback( | |||||
| (item: T) => { | |||||
| setSelected((prev) => { | |||||
| if (isSingleChecked(item)) { | |||||
| return prev.filter((i) => i !== item); | |||||
| } else { | |||||
| return [...prev, item]; | |||||
| } | |||||
| }); | |||||
| }, | |||||
| [isSingleChecked], | |||||
| ); | |||||
| return [ | |||||
| selected, | |||||
| setSelected, | |||||
| checked, | |||||
| indeterminate, | |||||
| checkAll, | |||||
| isSingleChecked, | |||||
| checkSingle, | |||||
| ] as const; | |||||
| }; | |||||
| @@ -0,0 +1,40 @@ | |||||
| import { debounce } from 'lodash'; | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| /** | |||||
| * 用于追踪 DOM 元素尺寸的 hook。 | |||||
| * | |||||
| * @param initialWidth - 初始宽度。 | |||||
| * @param initialHeight - 初始高度。 | |||||
| * @param deps - 依赖列表。 | |||||
| * @return 一个元组,包含 DOM 元素的 ref、当前宽度和当前高度。 | |||||
| */ | |||||
| export function useDomSize<T extends HTMLElement>( | |||||
| initialWidth: number, | |||||
| initialHeight: number, | |||||
| deps: React.DependencyList = [], | |||||
| ) { | |||||
| const domRef = useRef<T>(null); | |||||
| const [width, setWidth] = useState(initialWidth); | |||||
| const [height, setHeight] = useState(initialHeight); | |||||
| useEffect(() => { | |||||
| const setDomHeight = () => { | |||||
| if (domRef.current) { | |||||
| setHeight(domRef.current.offsetHeight); | |||||
| setWidth(domRef.current.offsetWidth); | |||||
| } | |||||
| }; | |||||
| const debounceFunc = debounce(setDomHeight, 100); | |||||
| setDomHeight(); | |||||
| window.addEventListener('resize', debounceFunc); | |||||
| return () => { | |||||
| window.removeEventListener('resize', debounceFunc); | |||||
| }; | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, deps); | |||||
| return [domRef, { width, height }] as const; | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| import { useEffect, useRef } from 'react'; | |||||
| /** | |||||
| * 当指定的条件为真时执行 Effect 函数。 | |||||
| * | |||||
| * @param effect - The effect function to execute. | |||||
| * @param when - The condition to trigger the effect. | |||||
| * @param deps - The dependencies for the effect. | |||||
| */ | |||||
| export const useEffectWhen = (effect: () => void, when: boolean, deps: React.DependencyList) => { | |||||
| const requestFn = useRef<(() => void) | undefined>(effect); | |||||
| useEffect(() => { | |||||
| requestFn.current = effect; | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, [...deps, effect]); | |||||
| useEffect(() => { | |||||
| if (when && requestFn.current) { | |||||
| requestFn.current(); | |||||
| requestFn.current = undefined; | |||||
| } | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, [...deps, when]); | |||||
| }; | |||||
| @@ -0,0 +1,24 @@ | |||||
| import { FormInstance } from 'antd'; | |||||
| import { useEffect, useRef } from 'react'; | |||||
| /** | |||||
| * 用于在 modal 关闭时重置 Form 表单的 hook。 | |||||
| * | |||||
| * @param form - Ant Design Form 表单实例 | |||||
| * @param open - modal 是否打开 | |||||
| */ | |||||
| export const useResetForm = (form: FormInstance, open: boolean) => { | |||||
| const prevOpenRef = useRef<boolean>(); | |||||
| useEffect(() => { | |||||
| prevOpenRef.current = open; | |||||
| }, [open]); | |||||
| const prevOpen = prevOpenRef.current; | |||||
| useEffect(() => { | |||||
| if (!open && prevOpen) { | |||||
| form.resetFields(); | |||||
| } | |||||
| }, [form, prevOpen, open]); | |||||
| }; | |||||
| @@ -0,0 +1,46 @@ | |||||
| import { parseJsonText } from '@/utils'; | |||||
| import { useCallback, useRef } from 'react'; | |||||
| export const useSSE = (onMessage: (data: any) => void) => { | |||||
| const evtSourceRef = useRef<EventSource | null>(null); | |||||
| const setupSSE = useCallback( | |||||
| (name: string, namespace: string) => { | |||||
| const { origin } = location; | |||||
| const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | |||||
| const evtSource = new EventSource( | |||||
| `${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`, | |||||
| { withCredentials: false }, | |||||
| ); | |||||
| evtSource.onmessage = (event) => { | |||||
| const data = event?.data; | |||||
| if (!data) { | |||||
| return; | |||||
| } | |||||
| const dataJson = parseJsonText(data); | |||||
| if (dataJson) { | |||||
| const nodes = dataJson?.result?.object?.status?.nodes; | |||||
| if (nodes) { | |||||
| onMessage(nodes); | |||||
| } | |||||
| } | |||||
| }; | |||||
| evtSource.onerror = (error) => { | |||||
| console.error('SSE error: ', error); | |||||
| }; | |||||
| evtSourceRef.current = evtSource; | |||||
| }, | |||||
| [onMessage], | |||||
| ); | |||||
| const closeSSE = useCallback(() => { | |||||
| if (evtSourceRef.current) { | |||||
| evtSourceRef.current.close(); | |||||
| evtSourceRef.current = null; | |||||
| } | |||||
| }, []); | |||||
| return [setupSSE, closeSSE]; | |||||
| }; | |||||
| @@ -0,0 +1,19 @@ | |||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| /** | |||||
| * 生成具有初始值的状态引用 | |||||
| * | |||||
| * @param initialValue - 状态的初始值 | |||||
| * @return 包含状态值、状态设置函数和可变引用对象的数组 | |||||
| */ | |||||
| export function useStateRef<T>(initialValue: T) { | |||||
| const [value, setValue] = useState(initialValue); | |||||
| const ref = useRef(value); | |||||
| useEffect(() => { | |||||
| ref.current = value; | |||||
| }, [value]); | |||||
| return [value, setValue, ref] as const; | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| import { useCallback, useEffect, useRef, useState } from 'react'; | |||||
| /** | |||||
| * 生成一个自定义钩子,用于管理模态框的可见性状态。 | |||||
| * | |||||
| * @param initialValue - 模态框的初始可见性状态。 | |||||
| * @return 一个数组,包含 visible、打开函数、关闭函数和 visible ref。 | |||||
| */ | |||||
| export function useVisible(initialValue: boolean) { | |||||
| const [visible, setVisible] = useState(initialValue); | |||||
| const ref = useRef(initialValue); | |||||
| const open = useCallback(() => { | |||||
| setVisible(true); | |||||
| }, []); | |||||
| const close = useCallback(() => { | |||||
| setVisible(false); | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| ref.current = visible; | |||||
| }, [visible]); | |||||
| return [visible, open, close, ref] as const; | |||||
| } | |||||
| @@ -1,6 +1,6 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useCheck } from '@/hooks'; | |||||
| import { useCheck } from '@/hooks/useCheck'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { type ExperimentInstance } from '@/types'; | import { type ExperimentInstance } from '@/types'; | ||||
| @@ -7,7 +7,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { AutoMLData } from '@/pages/AutoML/types'; | import { AutoMLData } from '@/pages/AutoML/types'; | ||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| @@ -1,5 +1,5 @@ | |||||
| import { CommonTabKeys } from '@/enums'; | import { CommonTabKeys } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { getAssetIcon } from '@/services/dataset/index.js'; | import { getAssetIcon } from '@/services/dataset/index.js'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { Flex, Tabs, type TabsProps } from 'antd'; | import { Flex, Tabs, type TabsProps } from 'antd'; | ||||
| @@ -6,7 +6,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { DevEditorStatus } from '@/enums'; | import { DevEditorStatus } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { | import { | ||||
| deleteEditorReq, | deleteEditorReq, | ||||
| getEditorListReq, | getEditorListReq, | ||||
| @@ -1,5 +1,6 @@ | |||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useStateRef, useVisible } from '@/hooks'; | |||||
| import { useStateRef } from '@/hooks/useStateRef'; | |||||
| import { useVisible } from '@/hooks/useVisible'; | |||||
| import { getExperimentIns } from '@/services/experiment/index.js'; | import { getExperimentIns } from '@/services/experiment/index.js'; | ||||
| import { getWorkflowById } from '@/services/pipeline/index.js'; | import { getWorkflowById } from '@/services/pipeline/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| @@ -1,6 +1,6 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useCheck } from '@/hooks'; | |||||
| import { useCheck } from '@/hooks/useCheck'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import { | import { | ||||
| deleteManyExperimentIns, | deleteManyExperimentIns, | ||||
| @@ -5,7 +5,7 @@ | |||||
| */ | */ | ||||
| import { ExperimentStatus } from '@/enums'; | import { ExperimentStatus } from '@/enums'; | ||||
| import { useStateRef } from '@/hooks'; | |||||
| import { useStateRef } from '@/hooks/useStateRef'; | |||||
| import { getExperimentPodsLog } from '@/services/experiment/index.js'; | import { getExperimentPodsLog } from '@/services/experiment/index.js'; | ||||
| import { DoubleRightOutlined, DownOutlined, UpOutlined } from '@ant-design/icons'; | import { DoubleRightOutlined, DownOutlined, UpOutlined } from '@ant-design/icons'; | ||||
| import { Button } from 'antd'; | import { Button } from 'antd'; | ||||
| @@ -1,7 +1,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import { ExperimentStatus, TensorBoardStatus } from '@/enums'; | import { ExperimentStatus, TensorBoardStatus } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { | import { | ||||
| deleteExperimentById, | deleteExperimentById, | ||||
| getExperiment, | getExperiment, | ||||
| @@ -1,6 +1,6 @@ | |||||
| import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; | ||||
| import { hyperParameterOptimizedMode } from '@/enums'; | import { hyperParameterOptimizedMode } from '@/enums'; | ||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | import { experimentStatusInfo } from '@/pages/Experiment/status'; | ||||
| import { | import { | ||||
| schedulerAlgorithms, | schedulerAlgorithms, | ||||
| @@ -7,8 +7,8 @@ import KFIcon from '@/components/KFIcon'; | |||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import SubAreaTitle from '@/components/SubAreaTitle'; | import SubAreaTitle from '@/components/SubAreaTitle'; | ||||
| import { MirrorVersionStatus } from '@/enums'; | import { MirrorVersionStatus } from '@/enums'; | ||||
| import { useDomSize } from '@/hooks'; | |||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { useDomSize } from '@/hooks/useDomSize'; | |||||
| import { | import { | ||||
| deleteMirrorVersionReq, | deleteMirrorVersionReq, | ||||
| getMirrorInfoReq, | getMirrorInfoReq, | ||||
| @@ -5,7 +5,7 @@ | |||||
| */ | */ | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { CommonTabKeys } from '@/enums'; | import { CommonTabKeys } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { deleteMirrorReq, getMirrorListReq } from '@/services/mirror'; | import { deleteMirrorReq, getMirrorListReq } from '@/services/mirror'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| @@ -4,12 +4,12 @@ | |||||
| * @Description: 模型演化 | * @Description: 模型演化 | ||||
| */ | */ | ||||
| import { useEffectWhen } from '@/hooks'; | |||||
| import { useEffectWhen } from '@/hooks/useEffectWhen'; | |||||
| import { getModelAtlasReq } from '@/services/dataset/index.js'; | import { getModelAtlasReq } from '@/services/dataset/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import G6, { G6GraphEvent, Graph, INode } from '@antv/g6'; | import G6, { G6GraphEvent, Graph, INode } from '@antv/g6'; | ||||
| import { useEffect, useRef, useState } from 'react'; | |||||
| import { useCallback, useEffect, useRef, useState } from 'react'; | |||||
| import NodeTooltips from '../NodeTooltips'; | import NodeTooltips from '../NodeTooltips'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| @@ -73,17 +73,45 @@ function ModelEvolution({ | |||||
| }; | }; | ||||
| }, []); | }, []); | ||||
| useEffectWhen( | |||||
| () => { | |||||
| if (version) { | |||||
| getModelAtlas(); | |||||
| } else { | |||||
| clearGraphData(); | |||||
| } | |||||
| }, | |||||
| isActive, | |||||
| [resourceId, version], | |||||
| ); | |||||
| const getModelAtlas = useCallback(async () => { | |||||
| // 请求失败或者版本不存在时,清除图形 | |||||
| function clearGraphData() { | |||||
| graph.data({ | |||||
| nodes: [], | |||||
| edges: [], | |||||
| }); | |||||
| graph.render(); | |||||
| graph.fitView(); | |||||
| } | |||||
| if (!resourceId || !identifier || !version) { | |||||
| clearGraphData(); | |||||
| return; | |||||
| } | |||||
| const params = { | |||||
| id: resourceId, | |||||
| identifier, | |||||
| version, | |||||
| }; | |||||
| const [res] = await to(getModelAtlasReq(params)); | |||||
| if (res && res.data) { | |||||
| const data = normalizeTreeData(res.data); | |||||
| apiData.current = data; | |||||
| hierarchyNodes.current = traverseHierarchically(data); | |||||
| const graphData = getGraphData(data, hierarchyNodes.current); | |||||
| graph.data(graphData); | |||||
| graph.render(); | |||||
| graph.fitView(); | |||||
| setShowNodeTooltip(false); | |||||
| setEnterTooltip(false); | |||||
| } else { | |||||
| clearGraphData(); | |||||
| } | |||||
| }, [resourceId, identifier, version]); | |||||
| useEffectWhen(getModelAtlas, isActive, [resourceId, identifier, version]); | |||||
| // 初始化图 | // 初始化图 | ||||
| const initGraph = () => { | const initGraph = () => { | ||||
| @@ -249,40 +277,6 @@ function ModelEvolution({ | |||||
| }, 100); | }, 100); | ||||
| }; | }; | ||||
| // 获取模型依赖 | |||||
| const getModelAtlas = async () => { | |||||
| const params = { | |||||
| id: resourceId, | |||||
| identifier, | |||||
| version, | |||||
| }; | |||||
| const [res] = await to(getModelAtlasReq(params)); | |||||
| if (res && res.data) { | |||||
| const data = normalizeTreeData(res.data); | |||||
| apiData.current = data; | |||||
| hierarchyNodes.current = traverseHierarchically(data); | |||||
| const graphData = getGraphData(data, hierarchyNodes.current); | |||||
| graph.data(graphData); | |||||
| graph.render(); | |||||
| graph.fitView(); | |||||
| setShowNodeTooltip(false); | |||||
| setEnterTooltip(false); | |||||
| } else { | |||||
| clearGraphData(); | |||||
| } | |||||
| }; | |||||
| // 请求失败或者版本不存在时,清除图形 | |||||
| function clearGraphData() { | |||||
| graph.data({ | |||||
| nodes: [], | |||||
| edges: [], | |||||
| }); | |||||
| graph.render(); | |||||
| graph.fitView(); | |||||
| } | |||||
| return ( | return ( | ||||
| <div className={styles['model-evolution']}> | <div className={styles['model-evolution']}> | ||||
| <div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div> | <div className={styles['model-evolution__graph']} id="canvas" ref={graphRef}></div> | ||||
| @@ -1,6 +1,6 @@ | |||||
| import SubAreaTitle from '@/components/SubAreaTitle'; | import SubAreaTitle from '@/components/SubAreaTitle'; | ||||
| import TableColTitle from '@/components/TableColTitle'; | import TableColTitle from '@/components/TableColTitle'; | ||||
| import { useCheck } from '@/hooks'; | |||||
| import { useCheck } from '@/hooks/useCheck'; | |||||
| import { getModelPageVersionsReq, getModelVersionsMetricsReq } from '@/services/dataset'; | import { getModelPageVersionsReq, getModelVersionsMetricsReq } from '@/services/dataset'; | ||||
| import { tableSorter } from '@/utils'; | import { tableSorter } from '@/utils'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| @@ -6,7 +6,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import { serviceTypeOptions } from '@/enums'; | import { serviceTypeOptions } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { deleteServiceReq, getServiceListReq } from '@/services/modelDeployment'; | import { deleteServiceReq, getServiceListReq } from '@/services/modelDeployment'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| @@ -8,8 +8,8 @@ import KFIcon from '@/components/KFIcon'; | |||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import SubAreaTitle from '@/components/SubAreaTitle'; | import SubAreaTitle from '@/components/SubAreaTitle'; | ||||
| import { ServiceRunStatus, serviceStatusOptions } from '@/enums'; | import { ServiceRunStatus, serviceStatusOptions } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { | import { | ||||
| deleteServiceVersionReq, | deleteServiceVersionReq, | ||||
| getServiceInfoReq, | getServiceInfoReq, | ||||
| @@ -1,6 +1,6 @@ | |||||
| import BasicInfo, { type BasicInfoData } from '@/components/BasicInfo'; | import BasicInfo, { type BasicInfoData } from '@/components/BasicInfo'; | ||||
| import { ServiceRunStatus } from '@/enums'; | import { ServiceRunStatus } from '@/enums'; | ||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { ServiceVersionData } from '@/pages/ModelDeployment/types'; | import { ServiceVersionData } from '@/pages/ModelDeployment/types'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { formatCodeConfig, formatModel } from '@/utils/format'; | import { formatCodeConfig, formatModel } from '@/utils/format'; | ||||
| @@ -1,6 +1,6 @@ | |||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import { ServiceRunStatus } from '@/enums'; | import { ServiceRunStatus } from '@/enums'; | ||||
| import { useComputingResource } from '@/hooks/resource'; | |||||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||||
| import { type ServiceVersionData } from '@/pages/ModelDeployment/types'; | import { type ServiceVersionData } from '@/pages/ModelDeployment/types'; | ||||
| import { getServiceVersionCompareReq } from '@/services/modelDeployment'; | import { getServiceVersionCompareReq } from '@/services/modelDeployment'; | ||||
| import { isEmpty } from '@/utils'; | import { isEmpty } from '@/utils'; | ||||
| @@ -1,5 +1,6 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { useStateRef, useVisible } from '@/hooks'; | |||||
| import { useStateRef } from '@/hooks/useStateRef'; | |||||
| import { useVisible } from '@/hooks/useVisible'; | |||||
| import { getWorkflowById, saveWorkflow } from '@/services/pipeline/index.js'; | import { getWorkflowById, saveWorkflow } from '@/services/pipeline/index.js'; | ||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { fittingString, parseJsonText, s8 } from '@/utils'; | import { fittingString, parseJsonText, s8 } from '@/utils'; | ||||
| @@ -1,7 +1,7 @@ | |||||
| import CodeSelectorModal from '@/components/CodeSelectorModal'; | import CodeSelectorModal from '@/components/CodeSelectorModal'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import ParameterInput, { requiredValidator } from '@/components/ParameterInput'; | import ParameterInput, { requiredValidator } from '@/components/ParameterInput'; | ||||
| import ParameterSelect from '@/components/ParameterSelect'; | |||||
| import ParameterSelect, { type ParameterSelectDataType } from '@/components/ParameterSelect'; | |||||
| import ResourceSelectorModal, { | import ResourceSelectorModal, { | ||||
| ResourceSelectorType, | ResourceSelectorType, | ||||
| selectorTypeConfig, | selectorTypeConfig, | ||||
| @@ -520,7 +520,8 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| {item.value.type === 'select' ? ( | {item.value.type === 'select' ? ( | ||||
| ['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? ( | ['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? ( | ||||
| <ParameterSelect | <ParameterSelect | ||||
| dataType={item.value.item_type as any} | |||||
| isPipeline | |||||
| dataType={item.value.item_type as ParameterSelectDataType} | |||||
| placeholder={item.value.placeholder} | placeholder={item.value.placeholder} | ||||
| /> | /> | ||||
| ) : null | ) : null | ||||
| @@ -1,7 +1,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import KFModal from '@/components/KFModal'; | import KFModal from '@/components/KFModal'; | ||||
| import PageTitle from '@/components/PageTitle'; | import PageTitle from '@/components/PageTitle'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { useCacheState } from '@/hooks/useCacheState'; | |||||
| import { | import { | ||||
| addWorkflow, | addWorkflow, | ||||
| cloneWorkflow, | cloneWorkflow, | ||||
| @@ -1,4 +1,4 @@ | |||||
| import { useDraggable } from '@/hooks/draggable'; | |||||
| import { useDraggable } from '@/hooks/useDraggable'; | |||||
| import { getWorkspaceOverviewReq } from '@/services/workspace'; | import { getWorkspaceOverviewReq } from '@/services/workspace'; | ||||
| import { ExperimentInstance } from '@/types'; | import { ExperimentInstance } from '@/types'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| @@ -4,7 +4,7 @@ | |||||
| * @Description: UI 公共方法 | * @Description: UI 公共方法 | ||||
| */ | */ | ||||
| import { PageEnum } from '@/enums/pagesEnums'; | import { PageEnum } from '@/enums/pagesEnums'; | ||||
| import { removeAllPageCacheState } from '@/hooks/pageCacheState'; | |||||
| import { removeAllPageCacheState } from '@/hooks/useCacheState'; | |||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { type ClientInfo } from '@/types'; | import { type ClientInfo } from '@/types'; | ||||
| import { history } from '@umijs/max'; | import { history } from '@umijs/max'; | ||||