| @@ -5,4 +5,5 @@ | |||
| public | |||
| dist | |||
| .umi | |||
| mock | |||
| mock | |||
| /src/iconfont/ | |||
| @@ -1,10 +1,16 @@ | |||
| module.exports = { | |||
| extends: [require.resolve('@umijs/lint/dist/config/eslint')], | |||
| extends: [ | |||
| require.resolve('@umijs/lint/dist/config/eslint'), | |||
| 'plugin:react/recommended', | |||
| "plugin:react-hooks/recommended" | |||
| ], | |||
| globals: { | |||
| page: true, | |||
| REACT_APP_ENV: true, | |||
| }, | |||
| rules: { | |||
| '@typescript-eslint/no-use-before-define': 'off', | |||
| 'react/react-in-jsx-scope': 'off', | |||
| 'react/display-name': 'off' | |||
| }, | |||
| }; | |||
| @@ -114,6 +114,7 @@ | |||
| "@umijs/max": "^4.0.66", | |||
| "cross-env": "^7.0.3", | |||
| "eslint": "^8.39.0", | |||
| "eslint-plugin-react-hooks": "~5.2.0", | |||
| "eslint-plugin-storybook": "~0.11.2", | |||
| "express": "^4.18.2", | |||
| "gh-pages": "^5.0.0", | |||
| @@ -168,7 +168,7 @@ export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => { | |||
| } | |||
| }; | |||
| export const patchRoutes: RuntimeConfig['patchRoutes'] = (e) => { | |||
| export const patchRoutes: RuntimeConfig['patchRoutes'] = () => { | |||
| //console.log('patchRoutes', e); | |||
| }; | |||
| @@ -232,7 +232,7 @@ export const antd: RuntimeAntdConfig = (memo) => { | |||
| memo.theme.components.Table = { | |||
| headerBg: 'rgba(242, 244, 247, 0.36)', | |||
| headerBorderRadius: 4, | |||
| rowSelectedBg: 'rgba(22, 100, 255, 0.05)', | |||
| // rowSelectedBg: 'rgba(22, 100, 255, 0.05)', 固定列时,横向滑动导致重叠 | |||
| }; | |||
| memo.theme.components.Tabs = { | |||
| titleFontSize: 16, | |||
| @@ -33,23 +33,23 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { | |||
| const [inputText, setInputText] = useState<string | undefined>(undefined); | |||
| useEffect(() => { | |||
| // 获取数据请求 | |||
| const getDataList = async () => { | |||
| const params = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| code_repo_name: searchText || undefined, | |||
| }; | |||
| const [res] = await to(getCodeConfigListReq(params)); | |||
| if (res && res.data && res.data.content) { | |||
| setDataList(res.data.content); | |||
| setTotal(res.data.totalElements); | |||
| } | |||
| }; | |||
| getDataList(); | |||
| }, [pagination, searchText]); | |||
| // 获取数据请求 | |||
| const getDataList = async () => { | |||
| const params = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| code_repo_name: searchText || undefined, | |||
| }; | |||
| const [res] = await to(getCodeConfigListReq(params)); | |||
| if (res && res.data && res.data.content) { | |||
| setDataList(res.data.content); | |||
| setTotal(res.data.totalElements); | |||
| } | |||
| }; | |||
| // 搜索 | |||
| const handleSearch = (value: string) => { | |||
| setSearchText(value); | |||
| @@ -45,23 +45,20 @@ type IframePageProps = { | |||
| function IframePage({ type, className, style }: IframePageProps) { | |||
| const [iframeUrl, setIframeUrl] = useState(''); | |||
| const [loading, setLoading] = useState(false); | |||
| useEffect(() => { | |||
| requestIframeUrl(); | |||
| return () => { | |||
| if (type === IframePageType.DevEnv) { | |||
| SessionStorage.removeItem(SessionStorage.editorUrlKey); | |||
| const requestIframeUrl = async () => { | |||
| setLoading(true); | |||
| const [res] = await to(getRequestAPI(type)()); | |||
| if (res && res.data) { | |||
| setIframeUrl(res.data); | |||
| } else { | |||
| setLoading(false); | |||
| } | |||
| }; | |||
| }, []); | |||
| const requestIframeUrl = async () => { | |||
| setLoading(true); | |||
| const [res] = await to(getRequestAPI(type)()); | |||
| if (res && res.data) { | |||
| setIframeUrl(res.data); | |||
| } else { | |||
| setLoading(false); | |||
| } | |||
| }; | |||
| requestIframeUrl(); | |||
| }, [type]); | |||
| const hideLoading = () => { | |||
| setLoading(false); | |||
| @@ -39,8 +39,20 @@ function ParameterSelect({ | |||
| const valueText = typeof value === 'object' && value !== null ? value.value : value; | |||
| useEffect(() => { | |||
| // 获取下拉数据 | |||
| const getSelectOptions = async () => { | |||
| if (!propsConfig) { | |||
| return; | |||
| } | |||
| const getOptions = propsConfig.getOptions; | |||
| const [res] = await to(getOptions()); | |||
| if (res) { | |||
| setOptions(res); | |||
| } | |||
| }; | |||
| getSelectOptions(); | |||
| }, []); | |||
| }, [propsConfig]); | |||
| const handleChange = (text: string) => { | |||
| if (typeof value === 'object' && value !== null) { | |||
| @@ -53,18 +65,7 @@ function ParameterSelect({ | |||
| } | |||
| }; | |||
| // 获取下拉数据 | |||
| const getSelectOptions = async () => { | |||
| if (!propsConfig) { | |||
| return; | |||
| } | |||
| const getOptions = propsConfig.getOptions; | |||
| const [res] = await to(getOptions()); | |||
| if (res) { | |||
| setOptions(res); | |||
| } | |||
| }; | |||
| // 只用于展示,FormInfo 组件带有 Tooltip | |||
| if (display) { | |||
| return ( | |||
| <FormInfo | |||
| @@ -72,7 +72,7 @@ function ResourceSelect({ | |||
| ]) as ResourceSelectorResponse; | |||
| setSelectedResource(originResource); | |||
| } | |||
| }, [value]); | |||
| }, [value, type]); | |||
| const selectResource = () => { | |||
| const resource = selectedResource; | |||
| @@ -91,16 +91,7 @@ function ResourceSelectorModal({ | |||
| const treeRef = useRef<TreeRef>(null); | |||
| const config = selectorTypeConfig[type]; | |||
| useEffect(() => { | |||
| setExpandedKeys([]); | |||
| setCheckedKeys([]); | |||
| setLoadedKeys([]); | |||
| setFiles([]); | |||
| setVersionPath(''); | |||
| setSearchText(''); | |||
| getTreeData(); | |||
| }, [activeTab, type]); | |||
| // 搜索 | |||
| const treeData = useMemo( | |||
| () => | |||
| originTreeData.filter((v) => | |||
| @@ -109,19 +100,45 @@ function ResourceSelectorModal({ | |||
| [originTreeData, searchText], | |||
| ); | |||
| // 获取数据集\模型\镜像列表 | |||
| const getTreeData = async () => { | |||
| const isPublic = activeTab === CommonTabKeys.Private ? false : true; | |||
| const [res] = await to(config.getList(isPublic)); | |||
| if (res) { | |||
| setOriginTreeData(res); | |||
| useEffect(() => { | |||
| // 获取数据集\模型\镜像列表 | |||
| const getTreeData = async () => { | |||
| const isPublic = activeTab === CommonTabKeys.Private ? false : true; | |||
| const [res] = await to(config.getList(isPublic)); | |||
| if (res) { | |||
| setOriginTreeData(res); | |||
| // 恢复上一次的 Expand 操作 | |||
| restoreLastExpand(); | |||
| } else { | |||
| setOriginTreeData([]); | |||
| } | |||
| }; | |||
| // 恢复上一次的 Expand 操作 | |||
| setFirstLoadList(true); | |||
| } else { | |||
| setOriginTreeData([]); | |||
| } | |||
| }; | |||
| setExpandedKeys([]); | |||
| setCheckedKeys([]); | |||
| setLoadedKeys([]); | |||
| setFiles([]); | |||
| setVersionPath(''); | |||
| setSearchText(''); | |||
| getTreeData(); | |||
| }, [activeTab, config]); | |||
| useEffect(() => { | |||
| // 恢复上一次的 Expand 操作 | |||
| // 判断是否有 defaultExpandedKeys,如果有,设置 expandedKeys | |||
| // fisrtLoadList 标志位 | |||
| const restoreLastExpand = () => { | |||
| if (firstLoadList && Array.isArray(defaultExpandedKeys) && defaultExpandedKeys.length > 0) { | |||
| setExpandedKeys(defaultExpandedKeys); | |||
| // 延时滑动到 defaultExpandedKeys,不然不会加载 defaultExpandedKeys,不然不会加载版本 | |||
| setTimeout(() => { | |||
| treeRef.current?.scrollTo({ key: defaultExpandedKeys[0], align: 'bottom' }); | |||
| }, 100); | |||
| } | |||
| }; | |||
| restoreLastExpand(); | |||
| }, [firstLoadList, defaultExpandedKeys]); | |||
| // 获取数据集\模型\镜像版本列表 | |||
| const getVersions = async (parentId: string, parentNode: any) => { | |||
| @@ -136,10 +153,10 @@ function ResourceSelectorModal({ | |||
| setLoadedKeys((prev) => prev.concat(parentId)); | |||
| } | |||
| // 恢复上一次的 Check 操作 | |||
| // 恢复上一次的 Check 操作,需要延时以便 TreeData 更新完 | |||
| setTimeout(() => { | |||
| restoreLastCheck(parentId, res); | |||
| }, 300); | |||
| }, 100); | |||
| } else { | |||
| setExpandedKeys([]); | |||
| return Promise.reject(error); | |||
| @@ -158,7 +175,7 @@ function ResourceSelectorModal({ | |||
| } | |||
| }; | |||
| // 动态加载 tree children | |||
| // 展开时,动态加载 tree children | |||
| const onLoadData = ({ key, children, ...rest }: TreeDataNode) => { | |||
| if (children) { | |||
| return Promise.resolve(); | |||
| @@ -187,42 +204,25 @@ function ResourceSelectorModal({ | |||
| } | |||
| }; | |||
| // 恢复上一次的 Expand 操作 | |||
| // 判断是否有 defaultExpandedKeys,如果有,设置 expandedKeys | |||
| // fisrtLoadList 标志位 | |||
| const restoreLastExpand = () => { | |||
| if (!firstLoadList && defaultExpandedKeys.length > 0) { | |||
| setTimeout(() => { | |||
| setExpandedKeys(defaultExpandedKeys); | |||
| setFirstLoadList(true); | |||
| setTimeout(() => { | |||
| treeRef.current?.scrollTo({ key: defaultExpandedKeys[0], align: 'bottom' }); | |||
| }, 100); | |||
| }, 0); | |||
| } | |||
| }; | |||
| // 恢复上一次的 Check 操作 | |||
| // 判断是否有 defaultCheckedKeys,如果有,设置 checkedKeys,并且调用获取文件列表接口 | |||
| // fisrtLoadVersions 标志位 | |||
| const restoreLastCheck = (parentId: string, versions: TreeDataNode[]) => { | |||
| if (!firstLoadVersions && defaultCheckedKeys.length > 0) { | |||
| if (!firstLoadVersions && Array.isArray(defaultCheckedKeys) && defaultCheckedKeys.length > 0) { | |||
| const last = defaultCheckedKeys[0] as string; | |||
| const { id } = getIdAndVersion(last); | |||
| // 判断正在打开的 id 和 defaultCheckedKeys 的 id 是否一致 | |||
| if (id === parentId) { | |||
| setCheckedKeys(defaultCheckedKeys); | |||
| const parentNode = versions.find((v) => v.key === last); | |||
| getFiles(last, parentNode); | |||
| setFirstLoadVersions(true); | |||
| setTimeout(() => { | |||
| setCheckedKeys(defaultCheckedKeys); | |||
| const parentNode = versions.find((v) => v.key === last); | |||
| getFiles(last, parentNode); | |||
| setFirstLoadVersions(true); | |||
| setTimeout(() => { | |||
| treeRef?.current?.scrollTo({ | |||
| key: defaultCheckedKeys[0], | |||
| align: 'bottom', | |||
| }); | |||
| }, 100); | |||
| }, 0); | |||
| treeRef?.current?.scrollTo({ | |||
| key: defaultCheckedKeys[0], | |||
| align: 'bottom', | |||
| }); | |||
| }, 100); | |||
| } | |||
| } | |||
| }; | |||
| @@ -105,6 +105,7 @@ export function useDomSize<T extends HTMLElement>( | |||
| return () => { | |||
| window.removeEventListener('resize', debounceFunc); | |||
| }; | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, [domRef, ...deps]); | |||
| return [domRef, { width, height }] as const; | |||
| @@ -136,10 +137,10 @@ export const useResetFormOnCloseModal = (form: FormInstance, open: boolean) => { | |||
| * Executes the effect function when the specified condition is true. | |||
| * | |||
| * @param effect - The effect function to execute. | |||
| * @param deps - The dependencies for the effect. | |||
| * @param when - The condition to trigger the effect. | |||
| * @param deps - The dependencies for the effect. | |||
| */ | |||
| export const useEffectWhen = (effect: () => void, deps: React.DependencyList, when: boolean) => { | |||
| export const useEffectWhen = (effect: () => void, when: boolean, deps: React.DependencyList) => { | |||
| const requestFns = useRef<(() => void)[]>([]); | |||
| useEffect(() => { | |||
| if (when) { | |||
| @@ -147,6 +148,7 @@ export const useEffectWhen = (effect: () => void, deps: React.DependencyList, wh | |||
| } else { | |||
| requestFns.current.splice(0, 1, effect); | |||
| } | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, deps); | |||
| useEffect(() => { | |||
| @@ -185,7 +187,7 @@ export const useCheck = <T>(list: T[]) => { | |||
| } | |||
| }); | |||
| }, | |||
| [selected, isSingleChecked], | |||
| [isSingleChecked], | |||
| ); | |||
| return [ | |||
| @@ -5,40 +5,39 @@ | |||
| */ | |||
| import { getComputingResourceReq } from '@/services/pipeline'; | |||
| import computingResourceState, { setComputingResource } from '@/state/computingResourceStore'; | |||
| import { ComputingResource } from '@/types'; | |||
| import { to } from '@/utils/promise'; | |||
| import { type SelectProps } from 'antd'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import { useSnapshot } from 'umi'; | |||
| const computingResource: ComputingResource[] = []; | |||
| // 获取资源规格 | |||
| export function useComputingResource() { | |||
| const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]); | |||
| const snap = useSnapshot(computingResourceState); | |||
| useEffect(() => { | |||
| if (snap.computingResource.length > 0) { | |||
| setResourceStandardList(snap.computingResource as ComputingResource[]); | |||
| // 获取资源规格列表数据 | |||
| const getComputingResource = async () => { | |||
| const params = { | |||
| page: 0, | |||
| size: 1000, | |||
| resource_type: '', | |||
| }; | |||
| const [res] = await to(getComputingResourceReq(params)); | |||
| if (res && res.data && res.data.content) { | |||
| setResourceStandardList(res.data.content); | |||
| computingResource.splice(0, computingResource.length, ...res.data.content); | |||
| } | |||
| }; | |||
| if (computingResource.length > 0) { | |||
| setResourceStandardList(computingResource); | |||
| } else { | |||
| getComputingResource(); | |||
| } | |||
| }, []); | |||
| // 获取资源规格列表数据 | |||
| const getComputingResource = useCallback(async () => { | |||
| const params = { | |||
| page: 0, | |||
| size: 1000, | |||
| resource_type: '', | |||
| }; | |||
| const [res] = await to(getComputingResourceReq(params)); | |||
| if (res && res.data && res.data.content) { | |||
| setResourceStandardList(res.data.content); | |||
| setComputingResource(res.data.content); | |||
| } | |||
| }, []); | |||
| // 过滤资源规格 | |||
| const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = | |||
| useCallback((input: string, option?: ComputingResource) => { | |||
| @@ -1,25 +0,0 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-11-06 14:53:37 | |||
| * @Description: SessionStorage hook | |||
| */ | |||
| import SessionStorage from '@/utils/sessionStorage'; | |||
| import { useEffect, useState } from 'react'; | |||
| // 读取缓存数据,组件卸载时清除缓存 | |||
| export function useSessionStorage<T>(key: string, isObject: boolean, initialValue: T) { | |||
| const [storage, setStorage] = useState<T>(initialValue); | |||
| useEffect(() => { | |||
| const res = SessionStorage.getItem(key, isObject); | |||
| if (res) { | |||
| setStorage(res); | |||
| } | |||
| return () => { | |||
| SessionStorage.removeItem(key); | |||
| }; | |||
| }, []); | |||
| return [storage]; | |||
| } | |||
| @@ -29,60 +29,60 @@ function CreateAutoML() { | |||
| const isCopy = pathname.includes('copy'); | |||
| useEffect(() => { | |||
| // 获取服务详情 | |||
| const getAutoMLInfo = async (id: number) => { | |||
| const [res] = await to(getAutoMLInfoReq({ id })); | |||
| if (res && res.data) { | |||
| const autoMLInfo: AutoMLData = res.data; | |||
| const { | |||
| include_classifier: include_classifier_str, | |||
| include_feature_preprocessor: include_feature_preprocessor_str, | |||
| include_regressor: include_regressor_str, | |||
| exclude_classifier: exclude_classifier_str, | |||
| exclude_feature_preprocessor: exclude_feature_preprocessor_str, | |||
| exclude_regressor: exclude_regressor_str, | |||
| metrics: metrics_str, | |||
| ml_name: ml_name_str, | |||
| ...rest | |||
| } = autoMLInfo; | |||
| const include_classifier = include_classifier_str?.split(',').filter(Boolean); | |||
| const include_feature_preprocessor = include_feature_preprocessor_str | |||
| ?.split(',') | |||
| .filter(Boolean); | |||
| const include_regressor = include_regressor_str?.split(',').filter(Boolean); | |||
| const exclude_classifier = exclude_classifier_str?.split(',').filter(Boolean); | |||
| const exclude_feature_preprocessor = exclude_feature_preprocessor_str | |||
| ?.split(',') | |||
| .filter(Boolean); | |||
| const exclude_regressor = exclude_regressor_str?.split(',').filter(Boolean); | |||
| const metricsObj = safeInvoke(parseJsonText)(metrics_str) ?? {}; | |||
| const metrics = Object.entries(metricsObj).map(([key, value]) => ({ | |||
| name: key, | |||
| value, | |||
| })); | |||
| const ml_name = isCopy ? `${ml_name_str}-copy` : ml_name_str; | |||
| const formData = { | |||
| ...rest, | |||
| include_classifier, | |||
| include_feature_preprocessor, | |||
| include_regressor, | |||
| exclude_classifier, | |||
| exclude_feature_preprocessor, | |||
| exclude_regressor, | |||
| metrics, | |||
| ml_name, | |||
| }; | |||
| form.setFieldsValue(formData); | |||
| } | |||
| }; | |||
| // 编辑,复制 | |||
| if (id && !Number.isNaN(id)) { | |||
| getAutoMLInfo(id); | |||
| } | |||
| }, [id]); | |||
| // 获取服务详情 | |||
| const getAutoMLInfo = async (id: number) => { | |||
| const [res] = await to(getAutoMLInfoReq({ id })); | |||
| if (res && res.data) { | |||
| const autoMLInfo: AutoMLData = res.data; | |||
| const { | |||
| include_classifier: include_classifier_str, | |||
| include_feature_preprocessor: include_feature_preprocessor_str, | |||
| include_regressor: include_regressor_str, | |||
| exclude_classifier: exclude_classifier_str, | |||
| exclude_feature_preprocessor: exclude_feature_preprocessor_str, | |||
| exclude_regressor: exclude_regressor_str, | |||
| metrics: metrics_str, | |||
| ml_name: ml_name_str, | |||
| ...rest | |||
| } = autoMLInfo; | |||
| const include_classifier = include_classifier_str?.split(',').filter(Boolean); | |||
| const include_feature_preprocessor = include_feature_preprocessor_str | |||
| ?.split(',') | |||
| .filter(Boolean); | |||
| const include_regressor = include_regressor_str?.split(',').filter(Boolean); | |||
| const exclude_classifier = exclude_classifier_str?.split(',').filter(Boolean); | |||
| const exclude_feature_preprocessor = exclude_feature_preprocessor_str | |||
| ?.split(',') | |||
| .filter(Boolean); | |||
| const exclude_regressor = exclude_regressor_str?.split(',').filter(Boolean); | |||
| const metricsObj = safeInvoke(parseJsonText)(metrics_str) ?? {}; | |||
| const metrics = Object.entries(metricsObj).map(([key, value]) => ({ | |||
| name: key, | |||
| value, | |||
| })); | |||
| const ml_name = isCopy ? `${ml_name_str}-copy` : ml_name_str; | |||
| const formData = { | |||
| ...rest, | |||
| include_classifier, | |||
| include_feature_preprocessor, | |||
| include_regressor, | |||
| exclude_classifier, | |||
| exclude_feature_preprocessor, | |||
| exclude_regressor, | |||
| metrics, | |||
| ml_name, | |||
| }; | |||
| form.setFieldsValue(formData); | |||
| } | |||
| }; | |||
| }, [id, form, isCopy]); | |||
| // 创建、更新、复制实验 | |||
| const createExperiment = async (formData: FormData) => { | |||
| @@ -19,18 +19,18 @@ function AutoMLInfo() { | |||
| const [autoMLInfo, setAutoMLInfo] = useState<AutoMLData | undefined>(undefined); | |||
| useEffect(() => { | |||
| // 获取自动机器学习详情 | |||
| const getAutoMLInfo = async () => { | |||
| const [res] = await to(getAutoMLInfoReq({ id: autoMLId })); | |||
| if (res && res.data) { | |||
| setAutoMLInfo(res.data); | |||
| } | |||
| }; | |||
| if (autoMLId) { | |||
| getAutoMLInfo(); | |||
| } | |||
| }, []); | |||
| // 获取自动机器学习详情 | |||
| const getAutoMLInfo = async () => { | |||
| const [res] = await to(getAutoMLInfoReq({ id: autoMLId })); | |||
| if (res && res.data) { | |||
| setAutoMLInfo(res.data); | |||
| } | |||
| }; | |||
| }, [autoMLId]); | |||
| return ( | |||
| <div className={styles['auto-ml-info']}> | |||
| @@ -25,36 +25,36 @@ type TableData = { | |||
| function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps) { | |||
| const [tableData, setTableData] = useState<TableData[]>([]); | |||
| useEffect(() => { | |||
| // 获取实验运行历史记录 | |||
| const getHistoryFile = async () => { | |||
| const [res] = await to(getFileReq(fileUrl)); | |||
| if (res) { | |||
| const data: any[] = res.data; | |||
| const list: TableData[] = data.map((item) => { | |||
| return { | |||
| id: item[0]?.[0], | |||
| accuracy: item[1]?.[5]?.accuracy, | |||
| duration: item[1]?.[5]?.duration, | |||
| train_loss: item[1]?.[5]?.train_loss, | |||
| status: item[1]?.[2]?.['__enum__']?.split('.')?.[1], | |||
| }; | |||
| }); | |||
| list.forEach((item) => { | |||
| if (!item.id) return; | |||
| const config = (res as any).configs?.[item.id]; | |||
| item.feature = config?.['feature_preprocessor:__choice__']; | |||
| item.althorithm = isClassification | |||
| ? config?.['classifier:__choice__'] | |||
| : config?.['regressor:__choice__']; | |||
| }); | |||
| setTableData(list); | |||
| } | |||
| }; | |||
| if (fileUrl) { | |||
| getHistoryFile(); | |||
| } | |||
| }, [fileUrl]); | |||
| // 获取实验运行历史记录 | |||
| const getHistoryFile = async () => { | |||
| const [res] = await to(getFileReq(fileUrl)); | |||
| if (res) { | |||
| const data: any[] = res.data; | |||
| const list: TableData[] = data.map((item) => { | |||
| return { | |||
| id: item[0]?.[0], | |||
| accuracy: item[1]?.[5]?.accuracy, | |||
| duration: item[1]?.[5]?.duration, | |||
| train_loss: item[1]?.[5]?.train_loss, | |||
| status: item[1]?.[2]?.['__enum__']?.split('.')?.[1], | |||
| }; | |||
| }); | |||
| list.forEach((item) => { | |||
| if (!item.id) return; | |||
| const config = (res as any).configs?.[item.id]; | |||
| item.feature = config?.['feature_preprocessor:__choice__']; | |||
| item.althorithm = isClassification | |||
| ? config?.['classifier:__choice__'] | |||
| : config?.['regressor:__choice__']; | |||
| }); | |||
| setTableData(list); | |||
| } | |||
| }; | |||
| }, [fileUrl, isClassification]); | |||
| const columns: TableProps<TableData>['columns'] = [ | |||
| { | |||
| @@ -53,7 +53,7 @@ function ExperimentInstanceComponent({ | |||
| if (allIntanceIds.length === 0) { | |||
| setSelectedIns([]); | |||
| } | |||
| }, [experimentInsList]); | |||
| }, [allIntanceIds, setSelectedIns]); | |||
| // 删除实验实例确认 | |||
| const handleRemove = (instance: ExperimentInstance) => { | |||
| @@ -28,7 +28,7 @@ import { | |||
| } from 'antd'; | |||
| import { type SearchProps } from 'antd/es/input'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import ExperimentInstance from '../ExperimentInstance'; | |||
| import { ExperimentListType, experimentListConfig } from './config'; | |||
| import styles from './index.less'; | |||
| @@ -58,12 +58,8 @@ function ExperimentList({ type }: ExperimentListProps) { | |||
| ); | |||
| const config = experimentListConfig[type]; | |||
| useEffect(() => { | |||
| getAutoMLList(); | |||
| }, [pagination, searchText]); | |||
| // 获取自主机器学习或超参数自动优化列表 | |||
| const getAutoMLList = async () => { | |||
| const getAutoMLList = useCallback(async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| @@ -76,7 +72,11 @@ function ExperimentList({ type }: ExperimentListProps) { | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| }, [pagination, searchText, config]); | |||
| useEffect(() => { | |||
| getAutoMLList(); | |||
| }, [getAutoMLList]); | |||
| // 搜索 | |||
| const onSearch: SearchProps['onSearch'] = (value) => { | |||
| @@ -22,19 +22,19 @@ function ExperimentResult({ fileUrl, imageUrl, modelPath }: ExperimentResultProp | |||
| }, [imageUrl]); | |||
| useEffect(() => { | |||
| // 获取实验运行历史记录 | |||
| const getResultFile = async () => { | |||
| const [res] = await to(getFileReq(fileUrl)); | |||
| if (res) { | |||
| setResult(res as any as string); | |||
| } | |||
| }; | |||
| if (fileUrl) { | |||
| getResultFile(); | |||
| } | |||
| }, [fileUrl]); | |||
| // 获取实验运行历史记录 | |||
| const getResultFile = async () => { | |||
| const [res] = await to(getFileReq(fileUrl)); | |||
| if (res) { | |||
| setResult(res as any as string); | |||
| } | |||
| }; | |||
| return ( | |||
| <div className={styles['experiment-result']}> | |||
| <InfoGroup title="实验结果" height={420} width="100%"> | |||
| @@ -13,7 +13,7 @@ import { openAntdModal } from '@/utils/modal'; | |||
| import { to } from '@/utils/promise'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { App, Button, Input, Pagination, PaginationProps } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import AddCodeConfigModal, { OperationType } from '../components/AddCodeConfigModal'; | |||
| import CodeConfigItem from '../components/CodeConfigItem'; | |||
| import styles from './index.less'; | |||
| @@ -50,12 +50,8 @@ function CodeConfigList() { | |||
| const [inputText, setInputText] = useState<string | undefined>(undefined); | |||
| const { message } = App.useApp(); | |||
| useEffect(() => { | |||
| getDataList(); | |||
| }, [pagination, searchText]); | |||
| // 获取数据请求 | |||
| const getDataList = async () => { | |||
| const getDataList = useCallback(async () => { | |||
| const params = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| @@ -69,7 +65,11 @@ function CodeConfigList() { | |||
| setDataList([]); | |||
| setTotal(0); | |||
| } | |||
| }; | |||
| }, [pagination, searchText]); | |||
| useEffect(() => { | |||
| getDataList(); | |||
| }, [getDataList]); | |||
| // 删除请求 | |||
| const deleteRecord = async (id: number) => { | |||
| @@ -18,7 +18,7 @@ import { to } from '@/utils/promise'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { useParams, useSearchParams } from '@umijs/max'; | |||
| import { App, Button, Flex, Select, Tabs } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import AddVersionModal from '../AddVersionModal'; | |||
| import ResourceIntro from '../ResourceIntro'; | |||
| import ResourceVersion from '../ResourceVersion'; | |||
| @@ -45,7 +45,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| // 模型演化传入的 tab | |||
| const defaultTab = searchParams.get('tab') || ResourceInfoTabKeys.Introduction; | |||
| // 模型演化传入的版本 | |||
| let versionParam = searchParams.get('version'); | |||
| const versionParam = searchParams.get('version'); | |||
| const name = searchParams.get('name') || ''; | |||
| const owner = searchParams.get('owner') || ''; | |||
| const identifier = searchParams.get('identifier') || ''; | |||
| @@ -57,63 +57,60 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| const typeName = config.name; // 数据集/模型 | |||
| const { message } = App.useApp(); | |||
| useEffect(() => { | |||
| getVersionList(); | |||
| }, [resourceId, owner, identifier]); | |||
| useEffect(() => { | |||
| if (version) { | |||
| getResourceDetail({ | |||
| id: resourceId, | |||
| owner, | |||
| name, | |||
| identifier, | |||
| version, | |||
| is_public: is_public, | |||
| }); | |||
| } | |||
| }, [version]); | |||
| // 获取详情 | |||
| const getResourceDetail = async (params: { | |||
| owner: string; | |||
| name: string; | |||
| id: number; | |||
| identifier: string; | |||
| version?: string; | |||
| is_public: boolean; | |||
| }) => { | |||
| const getResourceDetail = useCallback(async () => { | |||
| const params = { | |||
| id: resourceId, | |||
| owner, | |||
| name, | |||
| identifier, | |||
| version, | |||
| is_public, | |||
| }; | |||
| const request = config.getInfo; | |||
| const [res] = await to(request(params)); | |||
| if (res && res.data) { | |||
| setInfo(res.data); | |||
| } | |||
| }; | |||
| }, [config, resourceId, owner, name, identifier, version, is_public]); | |||
| // 获取版本列表 | |||
| const getVersionList = async () => { | |||
| const request = config.getVersions; | |||
| const [res] = await to( | |||
| request({ | |||
| owner, | |||
| identifier, | |||
| }), | |||
| ); | |||
| if (res && res.data && res.data.length > 0) { | |||
| setVersionList(res.data); | |||
| if ( | |||
| versionParam && | |||
| res.data.find((item: ResourceVersionData) => item.name === versionParam) | |||
| ) { | |||
| setVersion(versionParam); | |||
| versionParam = null; | |||
| const getVersionList = useCallback( | |||
| async (refresh: boolean) => { | |||
| const request = config.getVersions; | |||
| const [res] = await to( | |||
| request({ | |||
| owner, | |||
| identifier, | |||
| }), | |||
| ); | |||
| if (res && res.data && res.data.length > 0) { | |||
| setVersionList(res.data); | |||
| if ( | |||
| !refresh && | |||
| versionParam && | |||
| res.data.find((item: ResourceVersionData) => item.name === versionParam) | |||
| ) { | |||
| setVersion(versionParam); | |||
| } else { | |||
| setVersion(res.data[0].name); | |||
| } | |||
| } else { | |||
| setVersion(res.data[0].name); | |||
| setVersion(undefined); | |||
| } | |||
| } else { | |||
| setVersion(undefined); | |||
| }, | |||
| [config, owner, identifier, versionParam], | |||
| ); | |||
| useEffect(() => { | |||
| if (version) { | |||
| getResourceDetail(); | |||
| } | |||
| }; | |||
| }, [version, getResourceDetail]); | |||
| useEffect(() => { | |||
| getVersionList(false); | |||
| }, [getVersionList]); | |||
| // 新建版本 | |||
| const showModal = () => { | |||
| @@ -125,7 +122,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| identifier: info.identifier, | |||
| is_public: is_public, | |||
| onOk: () => { | |||
| getVersionList(); | |||
| getVersionList(true); | |||
| close(); | |||
| }, | |||
| }); | |||
| @@ -172,12 +169,12 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| const [res] = await to(request(params)); | |||
| if (res) { | |||
| message.success('删除成功'); | |||
| getVersionList(); | |||
| getVersionList(true); | |||
| } | |||
| }; | |||
| // 处理删除 | |||
| const hanldeDelete = () => { | |||
| const handleDelete = () => { | |||
| modalConfirm({ | |||
| title: '删除后,该版本将不可恢复', | |||
| content: '是否确认删除?', | |||
| @@ -268,7 +265,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| <Button | |||
| type="default" | |||
| style={{ marginLeft: 'auto', marginRight: 0 }} | |||
| onClick={hanldeDelete} | |||
| onClick={handleDelete} | |||
| icon={<KFIcon type="icon-shanchu" />} | |||
| disabled={!version} | |||
| danger | |||
| @@ -8,7 +8,7 @@ import { modalConfirm } from '@/utils/ui'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { App, Button, Input, Pagination, PaginationProps } from 'antd'; | |||
| import { pick } from 'lodash'; | |||
| import { Ref, forwardRef, useEffect, useImperativeHandle, useState } from 'react'; | |||
| import { Ref, forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react'; | |||
| import { CategoryData, ResourceData, ResourceType, resourceConfig } from '../../config'; | |||
| import AddDatasetModal from '../AddDatasetModal'; | |||
| import ResourceItem from '../ResourceItem'; | |||
| @@ -58,9 +58,30 @@ function ResourceList( | |||
| const { message } = App.useApp(); | |||
| const config = resourceConfig[resourceType]; | |||
| // 获取数据请求 | |||
| const getDataList = useCallback(async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| is_public: isPublic, | |||
| [config.typeParamKey]: dataType, | |||
| [config.tagParamKey]: dataTag, | |||
| name: searchText || undefined, | |||
| }; | |||
| const request = config.getList; | |||
| const [res] = await to(request(params)); | |||
| if (res && res.data && res.data.content) { | |||
| setDataList(res.data.content); | |||
| setTotal(res.data.totalElements); | |||
| } else { | |||
| setDataList([]); | |||
| setTotal(0); | |||
| } | |||
| }, [dataType, dataTag, pagination, searchText, isPublic, config]); | |||
| useEffect(() => { | |||
| getDataList(); | |||
| }, [resourceType, dataType, dataTag, pagination, searchText, isPublic]); | |||
| }, [getDataList]); | |||
| useImperativeHandle( | |||
| ref, | |||
| @@ -81,27 +102,6 @@ function ResourceList( | |||
| [], | |||
| ); | |||
| // 获取数据请求 | |||
| const getDataList = async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| is_public: isPublic, | |||
| [config.typeParamKey]: dataType, | |||
| [config.tagParamKey]: dataTag, | |||
| name: searchText || undefined, | |||
| }; | |||
| const request = config.getList; | |||
| const [res] = await to(request(params)); | |||
| if (res && res.data && res.data.content) { | |||
| setDataList(res.data.content); | |||
| setTotal(res.data.totalElements); | |||
| } else { | |||
| setDataList([]); | |||
| setTotal(0); | |||
| } | |||
| }; | |||
| // 删除请求 | |||
| const deleteRecord = async (params: { owner: string; identifier: string; repo_id?: number }) => { | |||
| const request = config.deleteRecord; | |||
| @@ -3,7 +3,7 @@ import { useCacheState } from '@/hooks/pageCacheState'; | |||
| import { getAssetIcon } from '@/services/dataset/index.js'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Flex, Tabs, type TabsProps } from 'antd'; | |||
| import { useEffect, useRef, useState } from 'react'; | |||
| import { useCallback, useEffect, useRef, useState } from 'react'; | |||
| import { CategoryData, ResourceType, resourceConfig } from '../../config'; | |||
| import CategoryList from '../CategoryList'; | |||
| import ResourceList, { ResourceListRef } from '../ResourceList'; | |||
| @@ -23,9 +23,31 @@ function ResourcePage({ resourceType }: ResourcePageProps) { | |||
| const dataListRef = useRef<ResourceListRef>(null); | |||
| const config = resourceConfig[resourceType]; | |||
| // 获取分类 | |||
| const getAssetIconList = useCallback( | |||
| async (name: string = '') => { | |||
| const params = { | |||
| name: name, | |||
| page: 0, | |||
| size: 10000, | |||
| }; | |||
| const [res] = await to(getAssetIcon(params)); | |||
| if (res && res.data && res.data.content) { | |||
| const { content } = res.data; | |||
| setTypeList( | |||
| content.filter((item: CategoryData) => Number(item.category_id) === config.typeValue), | |||
| ); | |||
| setTagList( | |||
| content.filter((item: CategoryData) => Number(item.category_id) === config.tagValue), | |||
| ); | |||
| } | |||
| }, | |||
| [config], | |||
| ); | |||
| useEffect(() => { | |||
| getAssetIconList(); | |||
| }, []); | |||
| }, [getAssetIconList]); | |||
| // 分类搜索 | |||
| const handleCategorySearch = (value: string) => { | |||
| @@ -42,25 +64,6 @@ function ResourcePage({ resourceType }: ResourcePageProps) { | |||
| setActiveTag((prev) => (prev === record.name ? undefined : record.name)); | |||
| }; | |||
| // 获取分类 | |||
| const getAssetIconList = async (name: string = '') => { | |||
| const params = { | |||
| name: name, | |||
| page: 0, | |||
| size: 10000, | |||
| }; | |||
| const [res] = await to(getAssetIcon(params)); | |||
| if (res && res.data && res.data.content) { | |||
| const { content } = res.data; | |||
| setTypeList( | |||
| content.filter((item: CategoryData) => Number(item.category_id) === config.typeValue), | |||
| ); | |||
| setTagList( | |||
| content.filter((item: CategoryData) => Number(item.category_id) === config.tagValue), | |||
| ); | |||
| } | |||
| }; | |||
| // 切换 Tab,重置数据 | |||
| const hanleTabChange: TabsProps['onChange'] = (value) => { | |||
| dataListRef.current?.reset(); | |||
| @@ -127,28 +127,28 @@ function VersionCompareModal({ | |||
| text: '版本描述', | |||
| }, | |||
| ], | |||
| [], | |||
| [resourceType], | |||
| ); | |||
| useEffect(() => { | |||
| getServiceVersionCompare(); | |||
| }, []); | |||
| // 获取对比数据 | |||
| const getServiceVersionCompare = async () => { | |||
| const params = { | |||
| versions, | |||
| identifier, | |||
| is_public, | |||
| owner, | |||
| repo_id, | |||
| // 获取对比数据 | |||
| const getServiceVersionCompare = async () => { | |||
| const params = { | |||
| versions, | |||
| identifier, | |||
| is_public, | |||
| owner, | |||
| repo_id, | |||
| }; | |||
| const request = config.compareVersion; | |||
| const [res] = await to(request(params)); | |||
| if (res && res.data) { | |||
| setCompareData(res.data); | |||
| } | |||
| }; | |||
| const request = config.compareVersion; | |||
| const [res] = await to(request(params)); | |||
| if (res && res.data) { | |||
| setCompareData(res.data); | |||
| } | |||
| }; | |||
| getServiceVersionCompare(); | |||
| }, [versions, identifier, is_public, owner, repo_id, config]); | |||
| // 获取值 | |||
| function getValue<T extends DatasetData | ModelData>( | |||
| @@ -29,7 +29,7 @@ import { | |||
| type TableProps, | |||
| } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import CreateMirrorModal from '../components/CreateMirrorModal'; | |||
| import EditorStatusCell from '../components/EditorStatusCell'; | |||
| import styles from './index.less'; | |||
| @@ -57,12 +57,8 @@ function EditorList() { | |||
| }, | |||
| ); | |||
| useEffect(() => { | |||
| getEditorList(); | |||
| }, [pagination]); | |||
| // 获取编辑器列表 | |||
| const getEditorList = async () => { | |||
| const getEditorList = useCallback(async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| @@ -73,7 +69,11 @@ function EditorList() { | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| }, [pagination]); | |||
| useEffect(() => { | |||
| getEditorList(); | |||
| }, [getEditorList]); | |||
| // 删除编辑器 | |||
| const deleteEditor = async (id: number) => { | |||
| @@ -46,27 +46,27 @@ function ExperimentComparison() { | |||
| }); | |||
| const { message } = App.useApp(); | |||
| const config = useMemo(() => comparisonConfig[comparisonType], [comparisonType]); | |||
| const config = comparisonConfig[comparisonType]; | |||
| useEffect(() => { | |||
| getComparisonData(); | |||
| }, [experimentId]); | |||
| // 获取对比数据列表 | |||
| const getComparisonData = async () => { | |||
| const request = | |||
| comparisonType === ComparisonType.Train ? getExpTrainInfosReq : getExpEvaluateInfosReq; | |||
| const params = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| // 获取对比数据列表 | |||
| const getComparisonData = async () => { | |||
| const request = | |||
| comparisonType === ComparisonType.Train ? getExpTrainInfosReq : getExpEvaluateInfosReq; | |||
| const params = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| }; | |||
| const [res] = await to(request(experimentId, params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| const [res] = await to(request(experimentId, params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| getComparisonData(); | |||
| }, [experimentId, pagination, comparisonType]); | |||
| // 获取对比 url | |||
| const getExpMetrics = async () => { | |||
| @@ -187,7 +187,7 @@ function ExperimentComparison() { | |||
| })), | |||
| }, | |||
| ]; | |||
| }, [tableData]); | |||
| }, [tableData, config]); | |||
| return ( | |||
| <div className={styles['experiment-comparison']}> | |||
| @@ -56,10 +56,6 @@ function ExperimentText() { | |||
| }; | |||
| }, []); | |||
| useEffect(() => { | |||
| propsDrawerOpenRef.current = propsDrawerOpen; | |||
| }, [propsDrawerOpen]); | |||
| // 获取流水线模版 | |||
| const getWorkflow = async () => { | |||
| const [res] = await to(getWorkflowById(locationParams.workflowId)); | |||
| @@ -90,6 +90,9 @@ const ExperimentDrawer = ({ | |||
| instanceNodeStatus, | |||
| workflowId, | |||
| instanceNodeStartTime, | |||
| experimentName, | |||
| experimentId, | |||
| pipelineId, | |||
| ], | |||
| ); | |||
| @@ -57,7 +57,7 @@ function ExperimentInstanceComponent({ | |||
| if (allIntanceIds.length === 0) { | |||
| setSelectedIns([]); | |||
| } | |||
| }, [experimentInsList]); | |||
| }, [allIntanceIds, setSelectedIns]); | |||
| // 删除实验实例确认 | |||
| const handleRemove = (instance: ExperimentInstance) => { | |||
| @@ -43,19 +43,18 @@ function ExperimentResult({ | |||
| : undefined; | |||
| useEffect(() => { | |||
| // 获取实验结果 | |||
| const getExperimentResult = async (params: any) => { | |||
| const [res] = await to(getNodeResult(params)); | |||
| if (res && res.data && Array.isArray(res.data)) { | |||
| const data = res.data.filter((item: ExperimentResultData) => item.value.length > 0); | |||
| setExperimentResults(data); | |||
| } else { | |||
| setExperimentResults([]); | |||
| } | |||
| }; | |||
| getExperimentResult({ id: `${experimentInsId}`, node_id: pipelineNodeId }); | |||
| }, []); | |||
| // 获取实验结果 | |||
| const getExperimentResult = async (params: any) => { | |||
| const [res] = await to(getNodeResult(params)); | |||
| if (res && res.data && Array.isArray(res.data)) { | |||
| const data = res.data.filter((item: ExperimentResultData) => item.value.length > 0); | |||
| setExperimentResults(data); | |||
| } else { | |||
| setExperimentResults([]); | |||
| } | |||
| }; | |||
| }, [experimentInsId, pipelineNodeId]); | |||
| // 下载 | |||
| const download = (path: string) => { | |||
| @@ -53,8 +53,21 @@ function ExportModelModal({ | |||
| }; | |||
| useEffect(() => { | |||
| // 获取数据集、模型列表 | |||
| const requestResourceList = async () => { | |||
| const params = { | |||
| page: 0, | |||
| size: 1000, | |||
| is_public: false, // 个人 | |||
| }; | |||
| const [res] = await to(config.getList(params)); | |||
| if (res && res.data) { | |||
| setResources(res.data.content || []); | |||
| } | |||
| }; | |||
| requestResourceList(); | |||
| }, []); | |||
| }, [config]); | |||
| // 获取选中的数据集、模型 | |||
| const getSelectedResource = (id: number | undefined) => { | |||
| @@ -84,19 +97,6 @@ function ExportModelModal({ | |||
| } | |||
| }; | |||
| // 获取数据集、模型列表 | |||
| const requestResourceList = async () => { | |||
| const params = { | |||
| page: 0, | |||
| size: 1000, | |||
| is_public: false, // 个人 | |||
| }; | |||
| const [res] = await to(config.getList(params)); | |||
| if (res && res.data) { | |||
| setResources(res.data.content || []); | |||
| } | |||
| }; | |||
| // 获取数据集、模型版本列表 | |||
| const getRecourceVersions = async (id: number) => { | |||
| const resource = getSelectedResource(id); | |||
| @@ -68,7 +68,7 @@ function LogGroup({ | |||
| document.removeEventListener('mouseup', mouseUp); | |||
| closeSocket(); | |||
| }; | |||
| }, []); | |||
| }, [setIsMouseDown]); | |||
| // 请求日志 | |||
| const requestExperimentPodsLog = async () => { | |||
| @@ -3,7 +3,7 @@ import { getQueryByExperimentLog } from '@/services/experiment/index.js'; | |||
| import { to } from '@/utils/promise'; | |||
| import classNames from 'classnames'; | |||
| import dayjs from 'dayjs'; | |||
| import React, { useEffect, useRef, useState } from 'react'; | |||
| import React, { useCallback, useEffect, useRef, useState } from 'react'; | |||
| import LogGroup from '../LogGroup'; | |||
| import styles from './index.less'; | |||
| @@ -46,19 +46,8 @@ function LogList({ | |||
| const [logGroups, setLogGroups] = useState<ExperimentLog[]>([]); | |||
| const retryRef = useRef(3); // 等待 2 秒,重试 3 次 | |||
| // 当实例节点运行状态不是 Pending,获取实验日志组 | |||
| useEffect(() => { | |||
| if ( | |||
| instanceNodeStatus && | |||
| instanceNodeStatus !== ExperimentStatus.Pending && | |||
| logGroups.length === 0 | |||
| ) { | |||
| getExperimentLog(); | |||
| } | |||
| }, [instanceNodeStatus]); | |||
| // 获取实验 Pods 组 | |||
| const getExperimentLog = async () => { | |||
| const getExperimentLog = useCallback(async () => { | |||
| const start_time = dayjs(instanceNodeStartTime).valueOf() * 1.0e6; | |||
| const params = { | |||
| task_id: pipelineNodeId, | |||
| @@ -95,7 +84,18 @@ function LogList({ | |||
| }, 2 * 1000); | |||
| } | |||
| } | |||
| }; | |||
| }, [pipelineNodeId, workflowId, instanceName, instanceNamespace, instanceNodeStartTime]); | |||
| // 当实例节点运行状态不是 Pending,获取实验日志组 | |||
| useEffect(() => { | |||
| if ( | |||
| instanceNodeStatus && | |||
| instanceNodeStatus !== ExperimentStatus.Pending && | |||
| logGroups.length === 0 | |||
| ) { | |||
| getExperimentLog(); | |||
| } | |||
| }, [getExperimentLog, logGroups, instanceNodeStatus]); | |||
| return ( | |||
| <div className={classNames(styles['log-list'], className)} id="log-list" style={style}> | |||
| @@ -20,7 +20,7 @@ import tableCellRender, { TableCellValueType } from '@/utils/table'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { App, Button, ConfigProvider, Dropdown, Input, Space, Table, Tooltip } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import { useNavigate } from 'react-router-dom'; | |||
| import { ComparisonType } from './Comparison/config'; | |||
| import AddExperimentModal from './components/AddExperimentModal'; | |||
| @@ -35,12 +35,6 @@ function Experiment() { | |||
| const navigate = useNavigate(); | |||
| const [experimentList, setExperimentList] = useState([]); | |||
| const [workflowList, setWorkflowList] = useState([]); | |||
| const [queryFlow, setQueryFlow] = useState({ | |||
| offset: 1, | |||
| page: 0, | |||
| size: 10000, | |||
| name: null, | |||
| }); | |||
| const [experimentId, setExperimentId] = useState(null); | |||
| const [experimentInList, setExperimentInList] = useState([]); | |||
| const [expandedRowKeys, setExpandedRowKeys] = useState(null); | |||
| @@ -61,18 +55,28 @@ function Experiment() { | |||
| const { message } = App.useApp(); | |||
| useEffect(() => { | |||
| // 获取流水线列表 | |||
| const getWorkflowList = async () => { | |||
| const queryFlow = { | |||
| offset: 1, | |||
| page: 0, | |||
| size: 10000, | |||
| name: null, | |||
| }; | |||
| const [res] = await to(getWorkflow(queryFlow)); | |||
| if (res && res.data && res.data.content) { | |||
| setWorkflowList(res.data.content); | |||
| } | |||
| }; | |||
| getWorkflowList(); | |||
| return () => { | |||
| clearExperimentInTimers(); | |||
| }; | |||
| }, []); | |||
| useEffect(() => { | |||
| getExperimentList(); | |||
| }, [pagination, searchText]); | |||
| // 获取实验列表 | |||
| const getExperimentList = async () => { | |||
| const getExperimentList = useCallback(async () => { | |||
| const params = { | |||
| page: pagination.current - 1, | |||
| size: pagination.pageSize, | |||
| @@ -88,15 +92,11 @@ function Experiment() { | |||
| setTotal(res.data.totalElements); | |||
| } | |||
| }; | |||
| }, [pagination, searchText]); | |||
| // 获取流水线列表 | |||
| const getWorkflowList = async () => { | |||
| const [res] = await to(getWorkflow(queryFlow)); | |||
| if (res && res.data && res.data.content) { | |||
| setWorkflowList(res.data.content); | |||
| } | |||
| }; | |||
| useEffect(() => { | |||
| getExperimentList(); | |||
| }, [getExperimentList]); | |||
| // 搜索 | |||
| const onSearch = (value) => { | |||
| @@ -26,37 +26,37 @@ function CreateHyperParameter() { | |||
| const isCopy = pathname.includes('copy'); | |||
| useEffect(() => { | |||
| // 获取服务详情 | |||
| const getHyperParameterInfo = async (id: number) => { | |||
| const [res] = await to(getRayInfoReq({ id })); | |||
| if (res && res.data) { | |||
| const info: HyperParameterData = res.data; | |||
| const { name: name_str, parameters, points_to_evaluate, ...rest } = info; | |||
| const name = isCopy ? `${name_str}-copy` : name_str; | |||
| if (parameters && Array.isArray(parameters)) { | |||
| parameters.forEach((item) => { | |||
| const paramName = getReqParamName(item.type); | |||
| item.range = item[paramName]; | |||
| item[paramName] = undefined; | |||
| }); | |||
| } | |||
| const formData = { | |||
| ...rest, | |||
| name, | |||
| parameters, | |||
| points_to_evaluate: points_to_evaluate ?? [], | |||
| }; | |||
| form.setFieldsValue(formData); | |||
| } | |||
| }; | |||
| // 编辑,复制 | |||
| if (id && !Number.isNaN(id)) { | |||
| getHyperParameterInfo(id); | |||
| } | |||
| }, [id]); | |||
| // 获取服务详情 | |||
| const getHyperParameterInfo = async (id: number) => { | |||
| const [res] = await to(getRayInfoReq({ id })); | |||
| if (res && res.data) { | |||
| const info: HyperParameterData = res.data; | |||
| const { name: name_str, parameters, points_to_evaluate, ...rest } = info; | |||
| const name = isCopy ? `${name_str}-copy` : name_str; | |||
| if (parameters && Array.isArray(parameters)) { | |||
| parameters.forEach((item) => { | |||
| const paramName = getReqParamName(item.type); | |||
| item.range = item[paramName]; | |||
| item[paramName] = undefined; | |||
| }); | |||
| } | |||
| const formData = { | |||
| ...rest, | |||
| name, | |||
| parameters, | |||
| points_to_evaluate: points_to_evaluate ?? [], | |||
| }; | |||
| form.setFieldsValue(formData); | |||
| } | |||
| }; | |||
| }, [id, form, isCopy]); | |||
| // 创建、更新、复制实验 | |||
| const createExperiment = async (formData: FormData) => { | |||
| @@ -8,7 +8,7 @@ import { getRayInfoReq } from '@/services/hyperParameter'; | |||
| import { safeInvoke } from '@/utils/functional'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useParams } from '@umijs/max'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import HyperParameterBasic from '../components/HyperParameterBasic'; | |||
| import { HyperParameterData } from '../types'; | |||
| import styles from './index.less'; | |||
| @@ -20,19 +20,19 @@ function HyperparameterInfo() { | |||
| undefined, | |||
| ); | |||
| useEffect(() => { | |||
| if (hyperparameterId) { | |||
| getHyperparameterInfo(); | |||
| } | |||
| }, []); | |||
| // 获取自动机器学习详情 | |||
| const getHyperparameterInfo = async () => { | |||
| const getHyperparameterInfo = useCallback(async () => { | |||
| const [res] = await to(getRayInfoReq({ id: hyperparameterId })); | |||
| if (res && res.data) { | |||
| setHyperparameterInfo(res.data); | |||
| } | |||
| }; | |||
| }, [hyperparameterId]); | |||
| useEffect(() => { | |||
| if (hyperparameterId) { | |||
| getHyperparameterInfo(); | |||
| } | |||
| }, [hyperparameterId, getHyperparameterInfo]); | |||
| return ( | |||
| <div className={styles['hyper-parameter-info']}> | |||
| @@ -32,7 +32,7 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) { | |||
| setTableData(trialList); | |||
| setLoading(false); | |||
| }, 500); | |||
| }, []); | |||
| }, [trialList]); | |||
| // 计算 column | |||
| const first: HyperParameterTrial | undefined = trialList ? trialList[0] : undefined; | |||
| @@ -12,19 +12,19 @@ function ExperimentResult({ fileUrl }: ExperimentResultProps) { | |||
| const [result, setResult] = useState<string | undefined>(''); | |||
| useEffect(() => { | |||
| // 获取实验运行历史记录 | |||
| const getResultFile = async () => { | |||
| const [res] = await to(getFileReq(fileUrl)); | |||
| if (res) { | |||
| setResult(res as any as string); | |||
| } | |||
| }; | |||
| if (fileUrl) { | |||
| getResultFile(); | |||
| } | |||
| }, [fileUrl]); | |||
| // 获取实验运行历史记录 | |||
| const getResultFile = async () => { | |||
| const [res] = await to(getFileReq(fileUrl)); | |||
| if (res) { | |||
| setResult(res as any as string); | |||
| } | |||
| }; | |||
| return ( | |||
| <div className={styles['experiment-result']}> | |||
| <InfoGroup title="最佳实验结果" width="100%"> | |||
| @@ -43,15 +43,6 @@ function HyperParameterBasic({ | |||
| }: HyperParameterBasicProps) { | |||
| const getResourceDescription = useComputingResource()[2]; | |||
| // 格式化资源规格 | |||
| const formatResource = (resource?: string) => { | |||
| if (!resource) { | |||
| return undefined; | |||
| } | |||
| return getResourceDescription(resource); | |||
| }; | |||
| const basicDatas: BasicInfoData[] = useMemo(() => { | |||
| if (!info) { | |||
| return []; | |||
| @@ -146,10 +137,10 @@ function HyperParameterBasic({ | |||
| { | |||
| label: '资源规格', | |||
| value: info.resource, | |||
| format: formatResource, | |||
| format: getResourceDescription, | |||
| }, | |||
| ]; | |||
| }, [info]); | |||
| }, [info, getResourceDescription]); | |||
| const instanceDatas = useMemo(() => { | |||
| if (!runStatus) { | |||
| @@ -65,7 +65,7 @@ function MirrorCreate() { | |||
| return () => { | |||
| SessionStorage.removeItem(SessionStorage.mirrorNameKey); | |||
| }; | |||
| }, []); | |||
| }, [form]); | |||
| // 创建公网、本地镜像 | |||
| const createPublicMirror = async (formData: FormData) => { | |||
| @@ -16,6 +16,7 @@ import { | |||
| } from '@/services/mirror'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import { safeInvoke } from '@/utils/functional'; | |||
| import { to } from '@/utils/promise'; | |||
| import SessionStorage from '@/utils/sessionStorage'; | |||
| import tableCellRender, { TableCellValueType } from '@/utils/table'; | |||
| @@ -32,7 +33,7 @@ import { | |||
| type TablePaginationConfig, | |||
| type TableProps, | |||
| } from 'antd'; | |||
| import { useEffect, useMemo, useState } from 'react'; | |||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | |||
| import MirrorStatusCell from '../components/MirrorStatusCell'; | |||
| import styles from './index.less'; | |||
| @@ -70,31 +71,28 @@ function MirrorInfo() { | |||
| ); | |||
| const { message } = App.useApp(); | |||
| const isPublic = useMemo(() => mirrorInfo.image_type === 1, [mirrorInfo]); | |||
| useEffect(() => { | |||
| getMirrorInfo(); | |||
| }, []); | |||
| useEffect(() => { | |||
| getMirrorVersionList(); | |||
| }, [pagination]); | |||
| const mirrorId = safeInvoke(Number)(urlParams.id); | |||
| // 获取镜像详情 | |||
| const getMirrorInfo = async () => { | |||
| const id = Number(urlParams.id); | |||
| const [res] = await to(getMirrorInfoReq(id)); | |||
| const getMirrorInfo = useCallback(async () => { | |||
| if (!mirrorId) { | |||
| return; | |||
| } | |||
| const [res] = await to(getMirrorInfoReq(mirrorId)); | |||
| if (res && res.data) { | |||
| setMirrorInfo(res.data); | |||
| } | |||
| }; | |||
| }, [mirrorId]); | |||
| // 获取镜像版本列表 | |||
| const getMirrorVersionList = async () => { | |||
| const id = Number(urlParams.id); | |||
| const getMirrorVersionList = useCallback(async () => { | |||
| if (!mirrorId) { | |||
| return; | |||
| } | |||
| const params = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| image_id: id, | |||
| image_id: mirrorId, | |||
| }; | |||
| const [res] = await to(getMirrorVersionListReq(params)); | |||
| if (res && res.data) { | |||
| @@ -102,7 +100,17 @@ function MirrorInfo() { | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| }, [mirrorId, pagination]); | |||
| // 获取镜像详情 | |||
| useEffect(() => { | |||
| getMirrorInfo(); | |||
| }, [getMirrorInfo]); | |||
| // 获取镜像版本列表 | |||
| useEffect(() => { | |||
| getMirrorVersionList(); | |||
| }, [getMirrorVersionList]); | |||
| // 删除镜像版本 | |||
| const deleteMirrorVersion = async (id: number) => { | |||
| @@ -26,7 +26,7 @@ import { | |||
| } from 'antd'; | |||
| import { type SearchProps } from 'antd/es/input'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| const mirrorTabItems = [ | |||
| @@ -65,9 +65,25 @@ function MirrorList() { | |||
| ); | |||
| const { message } = App.useApp(); | |||
| // 获取镜像列表 | |||
| const getMirrorList = useCallback(async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| name: searchText || undefined, | |||
| image_type: activeTab === CommonTabKeys.Public ? 1 : 0, | |||
| }; | |||
| const [res] = await to(getMirrorListReq(params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }, [activeTab, pagination, searchText]); | |||
| useEffect(() => { | |||
| getMirrorList(); | |||
| }, [activeTab, pagination, searchText]); | |||
| }, [getMirrorList]); | |||
| // 切换 Tab,重置数据 | |||
| const hanleTabChange: TabsProps['onChange'] = (value) => { | |||
| @@ -82,22 +98,6 @@ function MirrorList() { | |||
| setActiveTab(value); | |||
| }; | |||
| // 获取镜像列表 | |||
| const getMirrorList = async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| name: searchText || undefined, | |||
| image_type: activeTab === CommonTabKeys.Public ? 1 : 0, | |||
| }; | |||
| const [res] = await to(getMirrorListReq(params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| // 删除镜像 | |||
| const deleteMirror = async (id: number) => { | |||
| const [res] = await to(deleteMirrorReq(id)); | |||
| @@ -1,5 +1,5 @@ | |||
| import * as echarts from 'echarts'; | |||
| import { useEffect, useRef } from 'react'; | |||
| import { useEffect, useMemo, useRef } from 'react'; | |||
| import styles from './index.less'; | |||
| import './tooltip.css'; | |||
| @@ -74,77 +74,80 @@ function MetricsChart({ name, chartData }: MetricsChartProps) { | |||
| }; | |||
| }); | |||
| const options: echarts.EChartsOption = { | |||
| backgroundColor: backgroundColor, | |||
| title: { | |||
| show: false, | |||
| }, | |||
| tooltip: { | |||
| trigger: 'item', | |||
| padding: 10, | |||
| formatter: (params: any) => { | |||
| const { name: xTitle, data } = params; | |||
| return getTooltip('step', xTitle, name, data); | |||
| }, | |||
| }, | |||
| legend: { | |||
| bottom: 10, | |||
| icon: 'rect', | |||
| itemWidth: 10, | |||
| itemHeight: 10, | |||
| itemGap: 20, | |||
| textStyle: { | |||
| color: 'rgba(29, 29, 32, 0.75)', | |||
| fontSize: 12, | |||
| }, | |||
| }, | |||
| color: colors, | |||
| grid: { | |||
| left: '15', | |||
| right: '15', | |||
| top: '20', | |||
| bottom: '60', | |||
| containLabel: true, | |||
| }, | |||
| xAxis: { | |||
| type: 'category', | |||
| boundaryGap: true, | |||
| offset: 10, | |||
| data: xAxisData, | |||
| axisLabel: { | |||
| color: 'rgba(29, 29, 32, 0.75)', | |||
| fontSize: 12, | |||
| }, | |||
| axisTick: { | |||
| const options: echarts.EChartsOption = useMemo( | |||
| () => ({ | |||
| backgroundColor: backgroundColor, | |||
| title: { | |||
| show: false, | |||
| }, | |||
| axisLine: { | |||
| lineStyle: { | |||
| color: '#eaeaea', | |||
| width: 1, | |||
| tooltip: { | |||
| trigger: 'item', | |||
| padding: 10, | |||
| formatter: (params: any) => { | |||
| const { name: xTitle, data } = params; | |||
| return getTooltip('step', xTitle, name, data); | |||
| }, | |||
| }, | |||
| }, | |||
| yAxis: { | |||
| type: 'value', | |||
| axisLabel: { | |||
| color: 'rgba(29, 29, 32, 0.75)', | |||
| fontSize: 12, | |||
| margin: 15, | |||
| legend: { | |||
| bottom: 10, | |||
| icon: 'rect', | |||
| itemWidth: 10, | |||
| itemHeight: 10, | |||
| itemGap: 20, | |||
| textStyle: { | |||
| color: 'rgba(29, 29, 32, 0.75)', | |||
| fontSize: 12, | |||
| }, | |||
| }, | |||
| axisLine: { | |||
| show: false, | |||
| color: colors, | |||
| grid: { | |||
| left: '15', | |||
| right: '15', | |||
| top: '20', | |||
| bottom: '60', | |||
| containLabel: true, | |||
| }, | |||
| xAxis: { | |||
| type: 'category', | |||
| boundaryGap: true, | |||
| offset: 10, | |||
| data: xAxisData, | |||
| axisLabel: { | |||
| color: 'rgba(29, 29, 32, 0.75)', | |||
| fontSize: 12, | |||
| }, | |||
| axisTick: { | |||
| show: false, | |||
| }, | |||
| axisLine: { | |||
| lineStyle: { | |||
| color: '#eaeaea', | |||
| width: 1, | |||
| }, | |||
| }, | |||
| }, | |||
| splitLine: { | |||
| lineStyle: { | |||
| color: '#e4e4e4', | |||
| width: 1, | |||
| type: 'dashed', | |||
| yAxis: { | |||
| type: 'value', | |||
| axisLabel: { | |||
| color: 'rgba(29, 29, 32, 0.75)', | |||
| fontSize: 12, | |||
| margin: 15, | |||
| }, | |||
| axisLine: { | |||
| show: false, | |||
| }, | |||
| splitLine: { | |||
| lineStyle: { | |||
| color: '#e4e4e4', | |||
| width: 1, | |||
| type: 'dashed', | |||
| }, | |||
| }, | |||
| }, | |||
| }, | |||
| series: seriesData, | |||
| }; | |||
| series: seriesData, | |||
| }), | |||
| [name, seriesData, xAxisData], | |||
| ); | |||
| useEffect(() => { | |||
| // 创建一个echarts实例,返回echarts实例 | |||
| @@ -158,7 +161,7 @@ function MetricsChart({ name, chartData }: MetricsChartProps) { | |||
| // myChart.dispose() 销毁实例 | |||
| chart.dispose(); | |||
| }; | |||
| }, []); | |||
| }, [options]); | |||
| return ( | |||
| <div className={styles['metrics-chart']}> | |||
| @@ -77,8 +77,8 @@ function ModelEvolution({ | |||
| clearGraphData(); | |||
| } | |||
| }, | |||
| [resourceId, version], | |||
| isActive, | |||
| [resourceId, version], | |||
| ); | |||
| // 初始化图 | |||
| @@ -60,60 +60,61 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||
| ] = useCheck(allMetricsNames); | |||
| useEffect(() => { | |||
| if (selectedMetrics.length !== 0 && selectedRowKeys.length !== 0) { | |||
| getModelVersionsMetrics(); | |||
| } else { | |||
| setChartData(undefined); | |||
| } | |||
| }, [selectedMetrics, selectedRowKeys, identifier, resourceId]); | |||
| // 获取模型版本列表,带有参数和指标数据 | |||
| const getModelPageVersions = async () => { | |||
| const params = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| identifier: identifier, | |||
| owner: owner, | |||
| type: MetricsType.Train, | |||
| }; | |||
| const [res] = await to(getModelPageVersionsReq(params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| getModelPageVersions(); | |||
| }, [pagination, identifier, owner]); | |||
| // 版本切换,自动勾选当前版本 | |||
| useEffect(() => { | |||
| const curRow = tableData.find((item) => item.name === version); | |||
| if ( | |||
| curRow && | |||
| curRow.metrics_names && | |||
| curRow.metrics_names.length > 0 && | |||
| !selectedRowKeys.includes(version) | |||
| ) { | |||
| setSelectedRowKeys([version, ...selectedRowKeys]); | |||
| if (curRow && curRow.metrics_names && curRow.metrics_names.length > 0) { | |||
| setSelectedRowKeys((prev) => { | |||
| if (!prev.includes(version)) { | |||
| return [version, ...prev]; | |||
| } | |||
| return prev; | |||
| }); | |||
| } | |||
| }, [tableData, version]); | |||
| useEffect(() => { | |||
| getModelPageVersions(); | |||
| }, [pagination, identifier, owner]); | |||
| // 获取模型版本列表,带有参数和指标数据 | |||
| const getModelPageVersions = async () => { | |||
| const params = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| identifier: identifier, | |||
| owner: owner, | |||
| type: MetricsType.Train, | |||
| // 获取模型版本指标的图表数据 | |||
| const getModelVersionsMetrics = async () => { | |||
| const params = { | |||
| versions: selectedRowKeys, | |||
| metrics: selectedMetrics, | |||
| type: MetricsType.Train, | |||
| identifier: identifier, | |||
| repo_id: resourceId, | |||
| }; | |||
| const [res] = await to(getModelVersionsMetricsReq(params)); | |||
| if (res && res.data) { | |||
| setChartData(res.data); | |||
| } | |||
| }; | |||
| const [res] = await to(getModelPageVersionsReq(params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| const getModelVersionsMetrics = async () => { | |||
| const params = { | |||
| versions: selectedRowKeys, | |||
| metrics: selectedMetrics, | |||
| type: MetricsType.Train, | |||
| identifier: identifier, | |||
| repo_id: resourceId, | |||
| }; | |||
| const [res] = await to(getModelVersionsMetricsReq(params)); | |||
| if (res && res.data) { | |||
| setChartData(res.data); | |||
| if (selectedMetrics.length !== 0 && selectedRowKeys.length !== 0) { | |||
| getModelVersionsMetrics(); | |||
| } else { | |||
| setChartData(undefined); | |||
| } | |||
| }; | |||
| }, [selectedMetrics, selectedRowKeys, identifier, resourceId]); | |||
| // 分页切换 | |||
| const handleTableChange: TableProps<TableData>['onChange'] = ( | |||
| @@ -225,7 +226,14 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr | |||
| })), | |||
| }, | |||
| ]; | |||
| }, [tableData, selectedMetrics]); | |||
| }, [ | |||
| tableData, | |||
| checkAllMetrics, | |||
| checkSingleMetrics, | |||
| isSingleMetricsChecked, | |||
| metricsChecked, | |||
| metricsIndeterminate, | |||
| ]); | |||
| return ( | |||
| <div className={styles['model-metrics']}> | |||
| @@ -11,8 +11,8 @@ import { createServiceReq, getServiceInfoReq, updateServiceReq } from '@/service | |||
| import { to } from '@/utils/promise'; | |||
| import { useNavigate, useParams } from '@umijs/max'; | |||
| import { App, Button, Col, Form, Input, Row, Select } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { ServiceData, createServiceVersionMessage } from '../types'; | |||
| import { useEffect } from 'react'; | |||
| import { createServiceVersionMessage } from '../types'; | |||
| import styles from './index.less'; | |||
| // 表单数据 | |||
| @@ -25,30 +25,28 @@ export type FormData = { | |||
| function CreateService() { | |||
| const navigate = useNavigate(); | |||
| const [form] = Form.useForm(); | |||
| const [serviceInfo, setServiceInfo] = useState<ServiceData | undefined>(undefined); | |||
| const { message } = App.useApp(); | |||
| const params = useParams(); | |||
| const serviceId = params.serviceId; | |||
| useEffect(() => { | |||
| // 获取服务详情 | |||
| const getServiceInfo = async () => { | |||
| const [res] = await to(getServiceInfoReq(serviceId)); | |||
| if (res && res.data) { | |||
| const { service_type, service_name, description } = res.data; | |||
| form.setFieldsValue({ | |||
| service_type, | |||
| service_name, | |||
| description, | |||
| }); | |||
| } | |||
| }; | |||
| if (serviceId) { | |||
| getServiceInfo(); | |||
| } | |||
| }, []); | |||
| // 获取服务详情 | |||
| const getServiceInfo = async () => { | |||
| const [res] = await to(getServiceInfoReq(serviceId)); | |||
| if (res && res.data) { | |||
| setServiceInfo(res.data); | |||
| const { service_type, service_name, description } = res.data; | |||
| form.setFieldsValue({ | |||
| service_type, | |||
| service_name, | |||
| description, | |||
| }); | |||
| } | |||
| }; | |||
| }, [serviceId, form]); | |||
| // 创建、更新服务 | |||
| const createService = async (formData: FormData) => { | |||
| @@ -55,11 +55,11 @@ function CreateServiceVersion() { | |||
| const [operationType, setOperationType] = useState(ServiceOperationType.Create); | |||
| const [lastPage, setLastPage] = useState(CreateServiceVersionFrom.ServiceInfo); | |||
| const { message } = App.useApp(); | |||
| // const [serviceInfo, setServiceInfo] = useState<ServiceData | undefined>(undefined); | |||
| const [versionInfo, setVersionInfo] = useState<ServiceVersionData | undefined>(undefined); | |||
| const params = useParams(); | |||
| const serviceId = params.serviceId; | |||
| // 因为没有服务版本详情接口,需要从缓存中获取 | |||
| useEffect(() => { | |||
| const res: ServiceVersionCache | undefined = SessionStorage.getItem( | |||
| SessionStorage.serviceVersionInfoKey, | |||
| @@ -98,22 +98,21 @@ function CreateServiceVersion() { | |||
| return () => { | |||
| SessionStorage.removeItem(SessionStorage.serviceVersionInfoKey); | |||
| }; | |||
| }, []); | |||
| }, [form]); | |||
| useEffect(() => { | |||
| getServiceInfo(); | |||
| }, []); | |||
| // 获取服务详情,设置服务名称 | |||
| const getServiceInfo = async () => { | |||
| const [res] = await to(getServiceInfoReq(serviceId)); | |||
| if (res && res.data) { | |||
| form.setFieldsValue({ | |||
| service_name: res.data.service_name, | |||
| }); | |||
| } | |||
| }; | |||
| // 获取服务详情 | |||
| const getServiceInfo = async () => { | |||
| const [res] = await to(getServiceInfoReq(serviceId)); | |||
| if (res && res.data) { | |||
| // setServiceInfo(res.data); | |||
| form.setFieldsValue({ | |||
| service_name: res.data.service_name, | |||
| }); | |||
| } | |||
| }; | |||
| getServiceInfo(); | |||
| }, [serviceId, form]); | |||
| // 创建版本 | |||
| const createServiceVersion = async (formData: FormData) => { | |||
| @@ -26,7 +26,7 @@ import { | |||
| } from 'antd'; | |||
| import { type SearchProps } from 'antd/es/input'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import { | |||
| CreateServiceVersionFrom, | |||
| ServiceData, | |||
| @@ -53,19 +53,8 @@ function ModelDeployment() { | |||
| }, | |||
| ); | |||
| useEffect(() => { | |||
| window.addEventListener('message', handleMessage); | |||
| return () => { | |||
| window.removeEventListener('message', handleMessage); | |||
| }; | |||
| }, []); | |||
| useEffect(() => { | |||
| getServiceList(); | |||
| }, [pagination, searchText, serviceType]); | |||
| // 获取模型部署服务列表 | |||
| const getServiceList = async () => { | |||
| const getServiceList = useCallback(async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| @@ -78,7 +67,52 @@ function ModelDeployment() { | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| }, [pagination, searchText, serviceType]); | |||
| // 去创建服务版本 | |||
| const gotoCreateServiceVersion = useCallback( | |||
| (serviceId: number) => { | |||
| SessionStorage.setItem( | |||
| SessionStorage.serviceVersionInfoKey, | |||
| { | |||
| operationType: ServiceOperationType.Create, | |||
| lastPage: CreateServiceVersionFrom.CreateService, | |||
| }, | |||
| true, | |||
| ); | |||
| navigate(`serviceInfo/${serviceId}/createVersion`); | |||
| }, | |||
| [navigate], | |||
| ); | |||
| // 获取模型部署服务列表 | |||
| useEffect(() => { | |||
| getServiceList(); | |||
| }, [getServiceList]); | |||
| // 接收创建服务成功的消息 | |||
| useEffect(() => { | |||
| const handleMessage = (e: MessageEvent) => { | |||
| const { type, payload } = e.data; | |||
| if (type === createServiceVersionMessage) { | |||
| modalConfirm({ | |||
| title: '创建服务成功', | |||
| content: '是否创建服务版本?', | |||
| isDelete: false, | |||
| cancelText: '稍后创建', | |||
| onOk: () => { | |||
| gotoCreateServiceVersion(payload); | |||
| }, | |||
| }); | |||
| } | |||
| }; | |||
| window.addEventListener('message', handleMessage); | |||
| return () => { | |||
| window.removeEventListener('message', handleMessage); | |||
| }; | |||
| }, [gotoCreateServiceVersion]); | |||
| // 删除模型部署 | |||
| const deleteService = async (record: ServiceData) => { | |||
| @@ -145,35 +179,6 @@ function ModelDeployment() { | |||
| navigate(`serviceInfo/${record.id}`); | |||
| }; | |||
| const handleMessage = (e: MessageEvent) => { | |||
| const { type, payload } = e.data; | |||
| if (type === createServiceVersionMessage) { | |||
| modalConfirm({ | |||
| title: '创建服务成功', | |||
| content: '是否创建服务版本?', | |||
| isDelete: false, | |||
| cancelText: '稍后创建', | |||
| onOk: () => { | |||
| gotoCreateServiceVersion(payload); | |||
| }, | |||
| }); | |||
| } | |||
| }; | |||
| // 去创建服务版本 | |||
| const gotoCreateServiceVersion = (serviceId: number) => { | |||
| SessionStorage.setItem( | |||
| SessionStorage.serviceVersionInfoKey, | |||
| { | |||
| operationType: ServiceOperationType.Create, | |||
| lastPage: CreateServiceVersionFrom.CreateService, | |||
| }, | |||
| true, | |||
| ); | |||
| navigate(`serviceInfo/${serviceId}/createVersion`); | |||
| }; | |||
| // 分页切换 | |||
| const handleTableChange: TableProps<ServiceData>['onChange'] = ( | |||
| pagination, | |||
| @@ -36,7 +36,7 @@ import { | |||
| } from 'antd'; | |||
| import { type SearchProps } from 'antd/es/input'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import ServiceRunStatusCell from '../components/ModelDeployStatusCell'; | |||
| import VersionCompareModal from '../components/VersionCompareModal'; | |||
| import { | |||
| @@ -89,24 +89,16 @@ function ServiceInfo() { | |||
| ]; | |||
| const getResourceDescription = useComputingResource()[2]; | |||
| useEffect(() => { | |||
| getServiceInfo(); | |||
| }, []); | |||
| useEffect(() => { | |||
| getServiceVersions(); | |||
| }, [pagination, searchText, serviceStatus]); | |||
| // 获取服务详情 | |||
| const getServiceInfo = async () => { | |||
| const getServiceInfo = useCallback(async () => { | |||
| const [res] = await to(getServiceInfoReq(serviceId)); | |||
| if (res && res.data) { | |||
| setServiceInfo(res.data); | |||
| } | |||
| }; | |||
| }, [serviceId]); | |||
| // 获取服务版本列表 | |||
| const getServiceVersions = async () => { | |||
| const getServiceVersions = useCallback(async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| @@ -125,7 +117,15 @@ function ServiceInfo() { | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| }, [pagination, serviceStatus, searchText, serviceId]); | |||
| useEffect(() => { | |||
| getServiceInfo(); | |||
| }, [getServiceInfo]); | |||
| useEffect(() => { | |||
| getServiceVersions(); | |||
| }, [getServiceVersions]); | |||
| // 删除模型部署 | |||
| const deleteServiceVersion = async (record: ServiceVersionData) => { | |||
| @@ -29,16 +29,18 @@ function ServiceVersionInfo() { | |||
| const id = params.id; | |||
| useEffect(() => { | |||
| getServiceVersionInfo(); | |||
| }, []); | |||
| // 获取服务版本详情 | |||
| const getServiceVersionInfo = async () => { | |||
| const [res] = await to(getServiceVersionInfoReq(id)); | |||
| if (res && res.data) { | |||
| setVersionInfo(res.data); | |||
| } | |||
| }; | |||
| // 获取服务版本详情 | |||
| const getServiceVersionInfo = async () => { | |||
| const [res] = await to(getServiceVersionInfoReq(id)); | |||
| if (res && res.data) { | |||
| setVersionInfo(res.data); | |||
| if (id) { | |||
| getServiceVersionInfo(); | |||
| } | |||
| }; | |||
| }, [id]); | |||
| const tabItems = [ | |||
| { | |||
| @@ -54,28 +54,27 @@ function ServerLog({ info }: ServerLogProps) { | |||
| ]; | |||
| useEffect(() => { | |||
| // 获取服务日志 | |||
| const getModelDeploymentLog = async () => { | |||
| if (info && logTime && logTime.length === 2) { | |||
| const params = { | |||
| start_time: logTime[0], | |||
| end_time: logTime[1], | |||
| id: info.id, | |||
| }; | |||
| const [res] = await to(getServiceVersionLogReq(params)); | |||
| if (res && res.data) { | |||
| setLogData((prev) => [...prev, res.data]); | |||
| setHasMore(!!res.data.log_content); | |||
| // setTimeout(() => { | |||
| // scrollToBottom(); | |||
| // }, 100); | |||
| } | |||
| } | |||
| }; | |||
| getModelDeploymentLog(); | |||
| }, [info, logTime]); | |||
| // 获取服务日志 | |||
| const getModelDeploymentLog = async () => { | |||
| if (info && logTime && logTime.length === 2) { | |||
| const params = { | |||
| start_time: logTime[0], | |||
| end_time: logTime[1], | |||
| id: info.id, | |||
| }; | |||
| const [res] = await to(getServiceVersionLogReq(params)); | |||
| if (res && res.data) { | |||
| setLogData((prev) => [...prev, res.data]); | |||
| setHasMore(!!res.data.log_content); | |||
| // setTimeout(() => { | |||
| // scrollToBottom(); | |||
| // }, 100); | |||
| } | |||
| } | |||
| }; | |||
| // 搜索 | |||
| const handleSearch = () => { | |||
| setLogData([]); | |||
| @@ -10,20 +10,20 @@ type UserGuideProps = { | |||
| function UserGuide({ info }: UserGuideProps) { | |||
| const [docs, setDocs] = useState(''); | |||
| useEffect(() => { | |||
| // 获取服务文档 | |||
| const getModelDeploymentDocs = async () => { | |||
| if (info) { | |||
| const [res] = await to(getServiceVersionDocsReq(info.id)); | |||
| if (res && res.data && res.data.docs) { | |||
| setDocs(JSON.stringify(res.data.docs, null, 2)); | |||
| } | |||
| } | |||
| }; | |||
| getModelDeploymentDocs(); | |||
| }, [info]); | |||
| // 获取服务文档 | |||
| const getModelDeploymentDocs = async () => { | |||
| if (info) { | |||
| const [res] = await to(getServiceVersionDocsReq(info.id)); | |||
| if (res && res.data && res.data.docs) { | |||
| setDocs(JSON.stringify(res.data.docs, null, 2)); | |||
| } | |||
| } | |||
| }; | |||
| return <div className={styles['user-guide']}>{docs}</div>; | |||
| } | |||
| @@ -38,15 +38,6 @@ const formatEnvText = (env?: Record<string, string>) => { | |||
| function VersionBasicInfo({ info }: BasicInfoProps) { | |||
| const getResourceDescription = useComputingResource()[2]; | |||
| // 格式化资源规格 | |||
| const formatResource = (resource?: string) => { | |||
| if (!resource) { | |||
| return undefined; | |||
| } | |||
| return getResourceDescription(resource); | |||
| }; | |||
| const datas: BasicInfoData[] = [ | |||
| { | |||
| label: '服务名称', | |||
| @@ -78,7 +69,7 @@ function VersionBasicInfo({ info }: BasicInfoProps) { | |||
| { | |||
| label: '资源规格', | |||
| value: info?.resource, | |||
| format: formatResource, | |||
| format: getResourceDescription, | |||
| }, | |||
| { | |||
| label: '挂载路径', | |||
| @@ -106,20 +106,20 @@ function VersionCompareModal({ version1, version2, ...rest }: VersionCompareModa | |||
| ); | |||
| useEffect(() => { | |||
| getServiceVersionCompare(); | |||
| }, []); | |||
| // 获取对比数据 | |||
| const getServiceVersionCompare = async () => { | |||
| const params = { | |||
| id1: version1, | |||
| id2: version2, | |||
| // 获取对比数据 | |||
| const getServiceVersionCompare = async () => { | |||
| const params = { | |||
| id1: version1, | |||
| id2: version2, | |||
| }; | |||
| const [res] = await to(getServiceVersionCompareReq(params)); | |||
| if (res && res.data) { | |||
| setCompareData(res.data); | |||
| } | |||
| }; | |||
| const [res] = await to(getServiceVersionCompareReq(params)); | |||
| if (res && res.data) { | |||
| setCompareData(res.data); | |||
| } | |||
| }; | |||
| getServiceVersionCompare(); | |||
| }, [version1, version2]); | |||
| const { | |||
| version1: v1 = {} as ServiceVersionData, | |||
| @@ -55,7 +55,7 @@ const JobForm: React.FC<JobFormProps> = (props) => { | |||
| updateTime: props.values.updateTime, | |||
| remark: props.values.remark, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, statusOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -85,7 +85,7 @@ const EditPipeline = () => { | |||
| }; | |||
| // 保存 | |||
| const savePipeline = async (val) => { | |||
| const savePipeline = async (isBack) => { | |||
| const [globalParamRes, globalParamError] = await to(paramsDrawerRef.current.validateFields()); | |||
| if (globalParamError) { | |||
| message.error('全局参数配置有误'); | |||
| @@ -122,7 +122,7 @@ const EditPipeline = () => { | |||
| saveWorkflow(params).then((ret) => { | |||
| message.success('保存成功'); | |||
| setTimeout(() => { | |||
| if (val) { | |||
| if (isBack) { | |||
| navigate({ pathname: `/pipeline/template` }); | |||
| } | |||
| }, 500); | |||
| @@ -19,19 +19,23 @@ const GlobalParamsDrawer = forwardRef( | |||
| ({ open = false, onClose, globalParam = [] }: GlobalParamsDrawerProps, ref) => { | |||
| const [form] = Form.useForm(); | |||
| useImperativeHandle(ref, () => ({ | |||
| validateFields: async () => { | |||
| const [values, error] = await to(form.validateFields()); | |||
| if (!error && values) { | |||
| return values; | |||
| } else { | |||
| return Promise.reject(error); | |||
| } | |||
| }, | |||
| getFieldsValue: () => { | |||
| return form.getFieldsValue(); | |||
| }, | |||
| })); | |||
| useImperativeHandle( | |||
| ref, | |||
| () => ({ | |||
| validateFields: async () => { | |||
| const [values, error] = await to(form.validateFields()); | |||
| if (!error && values) { | |||
| return values; | |||
| } else { | |||
| return Promise.reject(error); | |||
| } | |||
| }, | |||
| getFieldsValue: () => { | |||
| return form.getFieldsValue(); | |||
| }, | |||
| }), | |||
| [form], | |||
| ); | |||
| // 处理参数类型变化 | |||
| const handleTypeChange = (name: NamePath) => { | |||
| @@ -21,47 +21,47 @@ const ModelMenu = ({ onComponentDragEnd }: ModelMenuProps) => { | |||
| const [collapseItems, setCollapseItems] = useState<CollapseProps['items']>([]); | |||
| useEffect(() => { | |||
| // 获取所有组件 | |||
| const getAllComponents = async () => { | |||
| const [res] = await to(getComponentAll()); | |||
| if (res && res.data) { | |||
| const menus = res.data as ModelMenuData[]; | |||
| setModelMenusList(menus); | |||
| const items = menus.map((item) => { | |||
| return { | |||
| key: item.key, | |||
| label: item.name, | |||
| children: item.value.map((ele) => { | |||
| return ( | |||
| <div | |||
| key={ele.id} | |||
| draggable="true" | |||
| onDragEnd={(e) => { | |||
| dragEnd(e, ele); | |||
| }} | |||
| className={Styles.collapseItem} | |||
| > | |||
| {ele.icon_path && ( | |||
| <img | |||
| style={{ height: '16px', marginRight: '15px' }} | |||
| src={`/assets/images/${ele.icon_path}.png`} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| )} | |||
| {ele.component_label} | |||
| </div> | |||
| ); | |||
| }), | |||
| }; | |||
| }); | |||
| setCollapseItems(items); | |||
| } | |||
| }; | |||
| getAllComponents(); | |||
| }, []); | |||
| // 获取所有组件 | |||
| const getAllComponents = async () => { | |||
| const [res] = await to(getComponentAll()); | |||
| if (res && res.data) { | |||
| const menus = res.data as ModelMenuData[]; | |||
| setModelMenusList(menus); | |||
| const items = menus.map((item) => { | |||
| return { | |||
| key: item.key, | |||
| label: item.name, | |||
| children: item.value.map((ele) => { | |||
| return ( | |||
| <div | |||
| key={ele.id} | |||
| draggable="true" | |||
| onDragEnd={(e) => { | |||
| dragEnd(e, ele); | |||
| }} | |||
| className={Styles.collapseItem} | |||
| > | |||
| {ele.icon_path && ( | |||
| <img | |||
| style={{ height: '16px', marginRight: '15px' }} | |||
| src={`/assets/images/${ele.icon_path}.png`} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| )} | |||
| {ele.component_label} | |||
| </div> | |||
| ); | |||
| }), | |||
| }; | |||
| }); | |||
| setCollapseItems(items); | |||
| } | |||
| }; | |||
| const dragEnd = (e: React.DragEvent<HTMLDivElement>, data: PipelineNodeModel) => { | |||
| onComponentDragEnd({ | |||
| ...data, | |||
| @@ -78,55 +78,59 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| setOpen(false); | |||
| }; | |||
| useImperativeHandle(ref, () => ({ | |||
| showDrawer( | |||
| model: PipelineNodeModel, | |||
| params: PipelineGlobalParam[], | |||
| parentNodes: INode[], | |||
| validate: boolean = false, | |||
| ) { | |||
| try { | |||
| const nodeData: PipelineNodeModelSerialize = { | |||
| ...model, | |||
| in_parameters: JSON.parse(model.in_parameters), | |||
| out_parameters: JSON.parse(model.out_parameters), | |||
| control_strategy: JSON.parse(model.control_strategy), | |||
| }; | |||
| // console.log('model', nodeData); | |||
| setStagingItem({ | |||
| ...nodeData, | |||
| }); | |||
| form.resetFields(); | |||
| form.setFieldsValue({ | |||
| ...nodeData, | |||
| }); | |||
| if (validate) { | |||
| form.validateFields(); | |||
| useImperativeHandle( | |||
| ref, | |||
| () => ({ | |||
| showDrawer( | |||
| model: PipelineNodeModel, | |||
| params: PipelineGlobalParam[], | |||
| parentNodes: INode[], | |||
| validate: boolean = false, | |||
| ) { | |||
| try { | |||
| const nodeData: PipelineNodeModelSerialize = { | |||
| ...model, | |||
| in_parameters: JSON.parse(model.in_parameters), | |||
| out_parameters: JSON.parse(model.out_parameters), | |||
| control_strategy: JSON.parse(model.control_strategy), | |||
| }; | |||
| // console.log('model', nodeData); | |||
| setStagingItem({ | |||
| ...nodeData, | |||
| }); | |||
| form.resetFields(); | |||
| form.setFieldsValue({ | |||
| ...nodeData, | |||
| }); | |||
| if (validate) { | |||
| form.validateFields(); | |||
| } | |||
| } catch (error) { | |||
| console.error('JSON.parse error: ', error); | |||
| } | |||
| } catch (error) { | |||
| console.error('JSON.parse error: ', error); | |||
| } | |||
| setOpen(true); | |||
| setOpen(true); | |||
| // 参数下拉菜单 | |||
| setMenuItems(createMenuItems(params, parentNodes)); | |||
| }, | |||
| close: () => { | |||
| onClose(); | |||
| }, | |||
| validateFields: async () => { | |||
| if (!open) { | |||
| return; | |||
| } | |||
| const [values, error] = await to(form.validateFields()); | |||
| if (!error && values) { | |||
| return values; | |||
| } else { | |||
| form.scrollToField((error as any)?.errorFields?.[0]?.name, { block: 'center' }); | |||
| return Promise.reject(error); | |||
| } | |||
| }, | |||
| })); | |||
| // 参数下拉菜单 | |||
| setMenuItems(createMenuItems(params, parentNodes)); | |||
| }, | |||
| close: () => { | |||
| onClose(); | |||
| }, | |||
| validateFields: async () => { | |||
| if (!open) { | |||
| return; | |||
| } | |||
| const [values, error] = await to(form.validateFields()); | |||
| if (!error && values) { | |||
| return values; | |||
| } else { | |||
| form.scrollToField((error as any)?.errorFields?.[0]?.name, { block: 'center' }); | |||
| return Promise.reject(error); | |||
| } | |||
| }, | |||
| }), | |||
| [form, open], | |||
| ); | |||
| // ref 类型选择 | |||
| const selectRefData = ( | |||
| @@ -15,7 +15,7 @@ import tableCellRender, { TableCellValueType } from '@/utils/table'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { App, Button, ConfigProvider, Form, Input, Space, Table } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import { useNavigate } from 'react-router-dom'; | |||
| import styles from './index.less'; | |||
| @@ -39,12 +39,8 @@ const Pipeline = () => { | |||
| ); | |||
| const { message } = App.useApp(); | |||
| useEffect(() => { | |||
| getList(); | |||
| }, [pagination, searchText]); | |||
| // 获取流水线模板列表 | |||
| const getList = () => { | |||
| const getList = useCallback(() => { | |||
| const params = { | |||
| page: pagination.current - 1, | |||
| size: pagination.pageSize, | |||
| @@ -56,7 +52,11 @@ const Pipeline = () => { | |||
| setTotal(res.data.totalElements); | |||
| } | |||
| }); | |||
| }; | |||
| }, [pagination, searchText]); | |||
| useEffect(() => { | |||
| getList(); | |||
| }, [getList]); | |||
| // 搜索 | |||
| const onSearch = (value) => { | |||
| @@ -133,7 +133,6 @@ const Pipeline = () => { | |||
| current, | |||
| pageSize, | |||
| }); | |||
| getList(); | |||
| }; | |||
| const columns = [ | |||
| @@ -42,7 +42,7 @@ const ConfigForm: React.FC<ConfigFormProps> = (props) => { | |||
| updateTime: props.values.updateTime, | |||
| remark: props.values.remark, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, configTypeOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -53,7 +53,7 @@ const DeptForm: React.FC<DeptFormProps> = (props) => { | |||
| updateBy: props.values.updateBy, | |||
| updateTime: props.values.updateTime, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, statusOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -41,7 +41,7 @@ const DictTypeForm: React.FC<DictTypeFormProps> = (props) => { | |||
| updateTime: props.values.updateTime, | |||
| remark: props.values.remark, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, statusOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -47,7 +47,7 @@ const DictDataForm: React.FC<DataFormProps> = (props) => { | |||
| updateTime: props.values.updateTime, | |||
| remark: props.values.remark, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, statusOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -187,7 +187,7 @@ const DictDataTableList: React.FC = () => { | |||
| } | |||
| }); | |||
| } | |||
| }, [dictId, dictType, params]); | |||
| }, [dictId, dictType, id]); | |||
| const columns: ProColumns<API.System.DictData>[] = [ | |||
| { | |||
| @@ -68,7 +68,7 @@ const MenuForm: React.FC<MenuFormProps> = (props) => { | |||
| updateTime: props.values.updateTime, | |||
| remark: props.values.remark, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, statusOptions, visibleOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -44,7 +44,7 @@ const NoticeForm: React.FC<NoticeFormProps> = (props) => { | |||
| updateTime: props.values.updateTime, | |||
| remark: props.values.remark, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, statusOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -42,7 +42,7 @@ const PostForm: React.FC<PostFormProps> = (props) => { | |||
| updateTime: props.values.updateTime, | |||
| remark: props.values.remark, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, statusOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -48,7 +48,7 @@ const DataScopeForm: React.FC<DataScopeFormProps> = (props) => { | |||
| dataScope: props.values.dataScope, | |||
| }); | |||
| setDataScopeType(props.values.dataScope); | |||
| }, [props.values]); | |||
| }, [props.values, deptCheckedKeys, form]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -52,7 +52,7 @@ const RoleForm: React.FC<RoleFormProps> = (props) => { | |||
| updateTime: props.values.updateTime, | |||
| remark: props.values.remark, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, statusOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -65,7 +65,7 @@ const UserForm: React.FC<UserFormProps> = (props) => { | |||
| gitLinkUsername: props.values.gitLinkUsername, | |||
| gitLinkPassword: props.values.gitLinkPassword, | |||
| }); | |||
| }, [form, props]); | |||
| }, [form, props, statusOptions]); | |||
| const intl = useIntl(); | |||
| const handleOk = () => { | |||
| @@ -49,7 +49,8 @@ const Login = () => { | |||
| } else { | |||
| form.setFieldsValue({ username: '', password: '', autoLogin: false }); | |||
| } | |||
| }, []); | |||
| }, [form]); | |||
| const getCaptchaCode = async () => { | |||
| const [res] = await to(getCaptchaImg()); | |||
| if (res) { | |||
| @@ -7,46 +7,48 @@ import styles from './index.less'; | |||
| function AssetsManagement() { | |||
| const [type, setType] = useState(CommonTabKeys.Public); | |||
| const [assetCounts, setAssetCounts] = useState<{ title: string; value: number }[]>([]); | |||
| useEffect(() => { | |||
| // 获取工作空间资产数量 | |||
| const getWorkspacAssetCount = async () => { | |||
| const params = { | |||
| isPublic: type === CommonTabKeys.Public, | |||
| }; | |||
| const [res] = await to(getWorkspaceAssetCountReq(params)); | |||
| if (res && res.data) { | |||
| const { component, dataset, image, model, workflow } = res.data; | |||
| const items = [ | |||
| { | |||
| title: '数据集', | |||
| value: dataset, | |||
| }, | |||
| { | |||
| title: '模型', | |||
| value: model, | |||
| }, | |||
| { | |||
| title: '镜像', | |||
| value: image, | |||
| }, | |||
| { | |||
| title: '组件', | |||
| value: component, | |||
| }, | |||
| // { | |||
| // title: '代码配置', | |||
| // value: 0, | |||
| // }, | |||
| { | |||
| title: '流水线模版', | |||
| value: workflow, | |||
| }, | |||
| ]; | |||
| setAssetCounts(items); | |||
| } | |||
| }; | |||
| getWorkspacAssetCount(); | |||
| }, [type]); | |||
| // 获取工作空间资产数量 | |||
| const getWorkspacAssetCount = async () => { | |||
| const params = { | |||
| isPublic: type === CommonTabKeys.Public, | |||
| }; | |||
| const [res] = await to(getWorkspaceAssetCountReq(params)); | |||
| if (res && res.data) { | |||
| const { component, dataset, image, model, workflow } = res.data; | |||
| const items = [ | |||
| { | |||
| title: '数据集', | |||
| value: dataset, | |||
| }, | |||
| { | |||
| title: '模型', | |||
| value: model, | |||
| }, | |||
| { | |||
| title: '镜像', | |||
| value: image, | |||
| }, | |||
| { | |||
| title: '组件', | |||
| value: component, | |||
| }, | |||
| // { | |||
| // title: '代码配置', | |||
| // value: 0, | |||
| // }, | |||
| { | |||
| title: '流水线模版', | |||
| value: workflow, | |||
| }, | |||
| ]; | |||
| setAssetCounts(items); | |||
| } | |||
| }; | |||
| return ( | |||
| <div className={styles['assets-management']}> | |||
| @@ -1,6 +1,6 @@ | |||
| import themes from '@/styles/theme.less'; | |||
| import * as echarts from 'echarts'; | |||
| import React, { useEffect, useRef } from 'react'; | |||
| import React, { useEffect, useMemo, useRef } from 'react'; | |||
| import styles from './index.less'; | |||
| const color1 = new echarts.graphic.LinearGradient( | |||
| @@ -94,102 +94,106 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) { | |||
| chartData.Running + | |||
| chartData.Succeeded + | |||
| chartData.Terminated; | |||
| const options: echarts.EChartsOption = { | |||
| title: { | |||
| show: true, | |||
| left: '29%', | |||
| top: 'center', | |||
| textAlign: 'center', | |||
| text: [`{a|${total}}`, '{b|实验状态}'].join('\n'), | |||
| textStyle: { | |||
| rich: { | |||
| a: { | |||
| color: themes['textColor'], | |||
| fontSize: 20, | |||
| fontWeight: 700, | |||
| lineHeight: 28, | |||
| }, | |||
| b: { | |||
| color: themes['textColorSecondary'], | |||
| fontSize: 10, | |||
| fontWeight: 'normal', | |||
| const options: echarts.EChartsOption = useMemo( | |||
| () => ({ | |||
| title: { | |||
| show: true, | |||
| left: '29%', | |||
| top: 'center', | |||
| textAlign: 'center', | |||
| text: [`{a|${total}}`, '{b|实验状态}'].join('\n'), | |||
| textStyle: { | |||
| rich: { | |||
| a: { | |||
| color: themes['textColor'], | |||
| fontSize: 20, | |||
| fontWeight: 700, | |||
| lineHeight: 28, | |||
| }, | |||
| b: { | |||
| color: themes['textColorSecondary'], | |||
| fontSize: 10, | |||
| fontWeight: 'normal', | |||
| }, | |||
| }, | |||
| }, | |||
| }, | |||
| }, | |||
| tooltip: { | |||
| trigger: 'item', | |||
| }, | |||
| legend: { | |||
| top: 'center', | |||
| right: '5%', | |||
| orient: 'vertical', | |||
| icon: 'circle', | |||
| itemWidth: 6, | |||
| itemGap: 20, | |||
| height: 100, | |||
| }, | |||
| color: [color1, color2, color3, color4, color5], | |||
| series: [ | |||
| { | |||
| type: 'pie', | |||
| radius: ['70%', '80%'], | |||
| center: ['30%', '50%'], | |||
| avoidLabelOverlap: false, | |||
| padAngle: 3, | |||
| itemStyle: { | |||
| borderRadius: 3, | |||
| }, | |||
| minAngle: 5, | |||
| label: { | |||
| show: false, | |||
| }, | |||
| emphasis: { | |||
| tooltip: { | |||
| trigger: 'item', | |||
| }, | |||
| legend: { | |||
| top: 'center', | |||
| right: '5%', | |||
| orient: 'vertical', | |||
| icon: 'circle', | |||
| itemWidth: 6, | |||
| itemGap: 20, | |||
| height: 100, | |||
| }, | |||
| color: [color1, color2, color3, color4, color5], | |||
| series: [ | |||
| { | |||
| type: 'pie', | |||
| radius: ['70%', '80%'], | |||
| center: ['30%', '50%'], | |||
| avoidLabelOverlap: false, | |||
| padAngle: 3, | |||
| itemStyle: { | |||
| borderRadius: 3, | |||
| }, | |||
| minAngle: 5, | |||
| label: { | |||
| show: false, | |||
| }, | |||
| emphasis: { | |||
| label: { | |||
| show: false, | |||
| }, | |||
| }, | |||
| labelLine: { | |||
| show: false, | |||
| }, | |||
| data: [ | |||
| { value: chartData.Failed > 0 ? chartData.Failed : undefined, name: '失败' }, | |||
| { value: chartData.Succeeded > 0 ? chartData.Succeeded : undefined, name: '成功' }, | |||
| { value: chartData.Terminated > 0 ? chartData.Terminated : undefined, name: '中止' }, | |||
| { value: chartData.Pending > 0 ? chartData.Pending : undefined, name: '等待' }, | |||
| { value: chartData.Running > 0 ? chartData.Running : undefined, name: '运行中' }, | |||
| ], | |||
| }, | |||
| labelLine: { | |||
| show: false, | |||
| }, | |||
| data: [ | |||
| { value: chartData.Failed > 0 ? chartData.Failed : undefined, name: '失败' }, | |||
| { value: chartData.Succeeded > 0 ? chartData.Succeeded : undefined, name: '成功' }, | |||
| { value: chartData.Terminated > 0 ? chartData.Terminated : undefined, name: '中止' }, | |||
| { value: chartData.Pending > 0 ? chartData.Pending : undefined, name: '等待' }, | |||
| { value: chartData.Running > 0 ? chartData.Running : undefined, name: '运行中' }, | |||
| ], | |||
| }, | |||
| { | |||
| type: 'pie', | |||
| radius: '60%', | |||
| center: ['30%', '50%'], | |||
| avoidLabelOverlap: false, | |||
| label: { | |||
| show: false, | |||
| }, | |||
| tooltip: { | |||
| show: false, | |||
| }, | |||
| emphasis: { | |||
| { | |||
| type: 'pie', | |||
| radius: '60%', | |||
| center: ['30%', '50%'], | |||
| avoidLabelOverlap: false, | |||
| label: { | |||
| show: false, | |||
| }, | |||
| disabled: true, | |||
| }, | |||
| animation: false, | |||
| labelLine: { | |||
| show: false, | |||
| }, | |||
| data: [ | |||
| { | |||
| value: 100, | |||
| itemStyle: { color: color6, borderColor: 'rgba(22, 100, 255, 0.08)', borderWidth: 1 }, | |||
| tooltip: { | |||
| show: false, | |||
| }, | |||
| ], | |||
| }, | |||
| ], | |||
| }; | |||
| emphasis: { | |||
| label: { | |||
| show: false, | |||
| }, | |||
| disabled: true, | |||
| }, | |||
| animation: false, | |||
| labelLine: { | |||
| show: false, | |||
| }, | |||
| data: [ | |||
| { | |||
| value: 100, | |||
| itemStyle: { color: color6, borderColor: 'rgba(22, 100, 255, 0.08)', borderWidth: 1 }, | |||
| }, | |||
| ], | |||
| }, | |||
| ], | |||
| }), | |||
| [chartData, total], | |||
| ); | |||
| useEffect(() => { | |||
| // 创建一个echarts实例,返回echarts实例 | |||
| @@ -203,7 +207,7 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) { | |||
| // myChart.dispose() 销毁实例 | |||
| chart.dispose(); | |||
| }; | |||
| }, []); | |||
| }, [options]); | |||
| return ( | |||
| <div className={styles['experiment-chart']} style={style}> | |||
| @@ -1,16 +0,0 @@ | |||
| import { ComputingResource } from '@/types'; | |||
| import { proxy } from 'umi'; | |||
| type ComputingResourceStore = { | |||
| computingResource: ComputingResource[]; | |||
| }; | |||
| const state = proxy<ComputingResourceStore>({ | |||
| computingResource: [], | |||
| }); | |||
| export const setComputingResource = (computingResource: ComputingResource[]) => { | |||
| state.computingResource = computingResource; | |||
| }; | |||
| export default state; | |||
| @@ -162,6 +162,7 @@ function renderText(text: any | undefined | null, ellipsis: boolean | 'auto') { | |||
| wordBreak: 'break-all', | |||
| display: 'inline-block', | |||
| maxWidth: '100%', | |||
| verticalAlign: 'middle', | |||
| } | |||
| : undefined | |||
| } | |||