feat: benchmark detail actions

This commit is contained in:
jialin
2026-02-01 22:56:36 +08:00
parent 0849a2f114
commit 9796233897
12 changed files with 268 additions and 103 deletions
@@ -10,6 +10,7 @@ type AddModalProps = {
action: PageActionType;
open: boolean;
currentData?: ListItem; // Used when action is EDIT
clusterList?: Global.BaseOption<number>[];
onOk: (values: FormData) => void;
onCancel: () => void;
};
@@ -18,6 +19,7 @@ const AddBenchmark: React.FC<AddModalProps> = ({
action,
open,
currentData,
clusterList,
onOk,
onCancel
}) => {
@@ -51,6 +53,7 @@ const AddBenchmark: React.FC<AddModalProps> = ({
action={action}
open={open}
currentData={currentData}
clusterList={clusterList}
onFinish={handleOk}
/>
</FormDrawer>
@@ -1,27 +1,18 @@
import IconFont from '@/components/icon-font';
import { useQueryClusterList } from '@/pages/cluster-management/services/use-query-cluster-list';
import { useIntl, useSearchParams } from '@umijs/max';
import { Tabs, TabsProps } from 'antd';
import React, { useEffect, useState } from 'react';
import DetailContext from '../config/detail-context';
import { BenchmarkListItem } from '../config/types';
import useQueryDetail from '../services/use-query-detail';
import React, { useState } from 'react';
import Environment from './environment';
import Summary from './summary';
const Details: React.FC<{ currentData?: BenchmarkListItem }> = ({
currentData
}) => {
const Details: React.FC<{
tabBarExtraContent: {
right: React.ReactNode;
};
}> = ({ tabBarExtraContent }) => {
const intl = useIntl();
const [searchParams] = useSearchParams();
const id = searchParams.get('id');
const { loading, detailData, cancelRequest, fetchData } = useQueryDetail();
const {
clusterList,
fetchClusterList,
cancelRequest: cancelClusterRequest
} = useQueryClusterList();
const [activeKey, setActiveKey] = useState('summary');
const [searchParams] = useSearchParams();
const items: TabsProps['items'] = [
{
@@ -36,45 +27,21 @@ const Details: React.FC<{ currentData?: BenchmarkListItem }> = ({
children: <Environment />,
icon: <IconFont type="icon-server02" />
}
// {
// key: 'logs',
// label: intl.formatMessage({ id: 'benchmark.detail.logs.title' }),
// children: <Logs />,
// icon: <IconFont type="icon-logs" />
// }
];
const handleChangeTab = (key: string) => {
setActiveKey(key);
};
useEffect(() => {
if (id) {
fetchData(id);
fetchClusterList({ page: -1 });
} else {
cancelRequest();
cancelClusterRequest();
}
}, [id]);
return (
<DetailContext.Provider
value={{
detailData: detailData || {},
clusterList: clusterList,
loading: loading,
id: Number(id)
}}
>
<Tabs
size="small"
activeKey={activeKey}
onChange={handleChangeTab}
items={items}
type="card"
/>
</DetailContext.Provider>
<Tabs
size="small"
activeKey={activeKey}
onChange={handleChangeTab}
items={items}
type="card"
tabBarExtraContent={tabBarExtraContent}
/>
);
};
@@ -0,0 +1,14 @@
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.fade-in {
animation-name: fade-in;
animation-fill-mode: both;
}
@@ -0,0 +1,29 @@
import React, { PropsWithChildren } from 'react';
import './fade-in.less';
type FadeInProps = PropsWithChildren<{
duration?: number;
delay?: number;
className?: string;
}>;
const FadeIn: React.FC<FadeInProps> = ({
children,
duration = 200,
delay = 0,
className
}) => {
return (
<div
className={`fade-in ${className || ''}`}
style={{
animationDuration: `${duration}ms`,
animationDelay: `${delay}ms`
}}
>
{children}
</div>
);
};
export default FadeIn;
@@ -53,15 +53,18 @@ const actionList = [
interface RowActionsProps {
record: ListItem;
page?: 'detail' | 'list';
handleSelect: (key: string, record: ListItem) => void;
}
const RowActions: React.FC<RowActionsProps> = (props) => {
const { record, handleSelect } = props;
const { record, handleSelect, page = 'list' } = props;
const { onDownloadLog, contextHolder } = useDownloadLogs();
const actions = actionList.filter((action) => {
if (action.status && action.status.length > 0) {
if (page === 'detail' && action.key === 'edit') {
return false;
} else if (action.status && action.status.length > 0) {
return action.status?.includes(record.state);
}
@@ -1,14 +1,9 @@
import AutoTooltip from '@/components/auto-tooltip';
import { useIntl } from '@umijs/max';
import { Descriptions, Flex, Tag } from 'antd';
import _ from 'lodash';
import React, { useMemo } from 'react';
import { useDetailContext } from '../../config/detail-context';
const calcTotalVram = (vram: Record<string, number>) => {
return _.sum(_.values(vram));
};
const Instance: React.FC = () => {
const intl = useIntl();
const { detailData } = useDetailContext();
+128 -3
View File
@@ -1,14 +1,45 @@
import DeleteModal from '@/components/delete-modal';
import BaseSelect from '@/components/seal-form/base/select';
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
import React, { useEffect } from 'react';
import { useMemoizedFn } from 'ahooks';
import React, { useEffect, useRef } from 'react';
import { PageContainerInner } from '../_components/page-box';
import PageBreadcrumb from '../_components/page-breadcrumb';
import { deleteBenchmark } from './apis';
import DetailContent from './components/detail-content';
import FadeIn from './components/fade-in';
import RowActions from './components/row-actions';
import ViewLogsModal from './components/view-logs-modal';
import DetailContext from './config/detail-context';
import { BenchmarkListItem } from './config/types';
import useViewLogs from './hooks/use-view-logs';
import useQueryBenchmarkList from './services/use-query-benchmarks';
import useQueryDetail from './services/use-query-detail';
import useStopBenchmark from './services/use-stop-benchmark';
const Details: React.FC = () => {
const modalRef = useRef<any>(null);
const navigate = useNavigate();
const intl = useIntl();
const {
dataList: benchmarkList,
fetchData: fetchBenchmarkList,
cancelRequest: cancelBenchmarkRequest
} = useQueryBenchmarkList();
const { loading, detailData, cancelRequest, fetchData } = useQueryDetail();
const { openViewLogsModal, closeViewLogsModal, openViewLogsModalStatus } =
useViewLogs();
const { handleStopBenchmark } = useStopBenchmark();
const [searchParams] = useSearchParams();
const name = searchParams.get('name');
const id = searchParams.get('id');
const handleOnChange = (value: number, option: any) => {
navigate(
`/models/benchmark/detail?id=${option.value}&name=${option.label}`,
{ state: 'benchmark-details', replace: true }
);
};
const breadcrumbItems = [
{
@@ -16,20 +47,114 @@ const Details: React.FC = () => {
onClick: () => navigate(-1)
},
{
title: name
title: (
<BaseSelect
size="small"
variant="borderless"
options={benchmarkList}
value={name}
style={{ minWidth: 100 }}
popupMatchSelectWidth={false}
onChange={handleOnChange}
></BaseSelect>
)
}
];
const handleDelete = (row: BenchmarkListItem) => {
modalRef.current?.show({
content: 'benchmark.title',
operation: 'common.delete.single.confirm',
name: row.name,
async onOk() {
await deleteBenchmark(row.id);
fetchBenchmarkList({ page: -1 }).then((items) => {
if (items.length === 0) {
navigate(-1);
} else {
handleOnChange(items[0].id, {
value: items[0].id,
label: items[0].name
});
}
});
}
});
};
const handleSelect = useMemoizedFn((val: any, row: BenchmarkListItem) => {
if (val === 'delete') {
handleDelete({ ...row, name: row.name });
} else if (val === 'viewlog') {
openViewLogsModal(row);
} else if (val === 'stop') {
handleStopBenchmark(row.id);
}
});
useEffect(() => {
document.title = `${intl.formatMessage({ id: 'benchmark.title' })} - ${name}`;
}, [name, intl]);
useEffect(() => {
fetchBenchmarkList({ page: -1 });
return () => {
cancelBenchmarkRequest();
};
}, []);
useEffect(() => {
if (id) {
fetchData(id);
} else {
cancelRequest();
}
}, [id]);
return (
<PageContainerInner
header={{
title: <PageBreadcrumb items={breadcrumbItems} />
}}
>
<DetailContent></DetailContent>
<DetailContext.Provider
value={{
detailData: detailData || {},
clusterList: [],
loading: loading,
id: Number(id)
}}
>
<DetailContent
tabBarExtraContent={{
right: (
<FadeIn>
<div
style={{
opacity: loading ? 0 : 1,
position: 'relative',
bottom: -2,
right: -1
}}
>
<RowActions
page="detail"
record={detailData as any}
handleSelect={handleSelect}
></RowActions>
</div>
</FadeIn>
)
}}
></DetailContent>
</DetailContext.Provider>
<ViewLogsModal
open={openViewLogsModalStatus.open}
url={openViewLogsModalStatus.url}
tail={openViewLogsModalStatus.tail}
onCancel={closeViewLogsModal}
></ViewLogsModal>
<DeleteModal ref={modalRef}></DeleteModal>
</PageContainerInner>
);
};
+8 -20
View File
@@ -3,7 +3,6 @@ import SealSelect from '@/components/seal-form/seal-select';
import { PageAction } from '@/config';
import useAppUtils from '@/hooks/use-app-utils';
import { ClusterStatusValueMap } from '@/pages/cluster-management/config';
import { useQueryClusterList } from '@/pages/cluster-management/services/use-query-cluster-list';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import React, { useEffect } from 'react';
@@ -15,13 +14,7 @@ const BasicForm: React.FC = () => {
const intl = useIntl();
const form = Form.useFormInstance();
const { getRuleMessage } = useAppUtils();
const { action, open } = useFormContext();
const {
loading: clusterLoading,
fetchClusterList,
cancelRequest: cancelClusterRequest,
clusterList
} = useQueryClusterList();
const { action, open, clusterList } = useFormContext();
useEffect(() => {
const initClusterId = (list: any[]) => {
@@ -37,18 +30,14 @@ const BasicForm: React.FC = () => {
return cluster_id;
};
fetchClusterList({ page: -1 }).then((list) => {
if (list.length > 0 && action === PageAction.CREATE) {
form.setFieldValue('cluster_id', initClusterId(list));
}
});
}, [form, action]);
useEffect(() => {
if (!open) {
cancelClusterRequest();
if (
clusterList &&
clusterList?.length > 0 &&
action === PageAction.CREATE
) {
form.setFieldValue('cluster_id', initClusterId(clusterList));
}
}, [open]);
}, [form, action, clusterList]);
return (
<>
@@ -78,7 +67,6 @@ const BasicForm: React.FC = () => {
>
<SealSelect
disabled={action === PageAction.EDIT}
loading={clusterLoading}
options={clusterList}
label={intl.formatMessage({ id: 'clusters.title' })}
required
+4 -2
View File
@@ -23,6 +23,7 @@ interface ProviderFormProps {
action: PageActionType;
currentData?: ListItem; // Used when action is EDIT
open?: boolean;
clusterList?: Global.BaseOption<number>[];
onFinish: (values: FormData) => Promise<void>;
}
@@ -33,7 +34,7 @@ const TABKeysMap = {
};
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
const { action, currentData, onFinish, open } = props;
const { action, currentData, onFinish, open, clusterList } = props;
const intl = useIntl();
const [form] = Form.useForm();
const profile = Form.useWatch('profile', form);
@@ -109,7 +110,8 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
<FormContext.Provider
value={{
action,
open
open,
clusterList: clusterList
}}
>
<Form
@@ -29,9 +29,12 @@ const defaultColumns: string[] = [
];
const fixedColumns: string[] = [];
const useColumnSettings = (options: { contentHeight: number }) => {
const useColumnSettings = (options: {
contentHeight: number;
clusterList: Global.BaseOption<number>[];
}) => {
const intl = useIntl();
const { contentHeight } = options;
const { contentHeight, clusterList } = options;
const [selectedColumns, setSelectedColumns] =
React.useState<string[]>(defaultColumns);
@@ -241,22 +244,22 @@ const useColumnSettings = (options: { contentHeight: number }) => {
title: renderTitle(intl.formatMessage({ id: 'clusters.title' })),
pos: 1,
dataIndex: 'cluster_id',
render: (text: string) => (
render: (text: number) => (
<AutoTooltip ghost minWidth={20}>
{text}
</AutoTooltip>
)
},
{
title: renderTitle(intl.formatMessage({ id: 'resources.worker' })),
pos: 2,
dataIndex: 'worker_id',
render: (text: string) => (
<AutoTooltip ghost minWidth={20}>
{text}
{clusterList?.find((item) => item.value === text)?.label || text}
</AutoTooltip>
)
},
// {
// title: renderTitle(intl.formatMessage({ id: 'resources.worker' })),
// pos: 2,
// dataIndex: 'worker_id',
// render: (text: string) => (
// <AutoTooltip ghost minWidth={20}>
// {text}
// </AutoTooltip>
// )
// },
{
title: renderTitle(
intl.formatMessage({ id: 'benchmark.detail.modelName' })
@@ -364,9 +367,8 @@ const useColumnSettings = (options: { contentHeight: number }) => {
);
// Sort by pos
selected.sort((a, b) => (a.pos || 0) - (b.pos || 0));
console.log('columns===', selected);
return selected;
}, [selectedColumns]);
}, [selectedColumns, clusterList]);
const SettingsButton = (
<ColumnSettings
+19 -7
View File
@@ -12,6 +12,7 @@ import _ from 'lodash';
import { useEffect } from 'react';
import NoResult from '../_components/no-result';
import PageBox from '../_components/page-box';
import { useQueryClusterList } from '../cluster-management/services/use-query-cluster-list';
import {
BENCHMARKS_API,
createBenchmark,
@@ -63,17 +64,26 @@ const Benchmark: React.FC = () => {
});
const { openViewLogsModal, closeViewLogsModal, openViewLogsModalStatus } =
useViewLogs();
const { SettingsButton, columns: selectedColumns } = useColumnSettings({
contentHeight: 320
});
const { handleStopBenchmark } = useStopBenchmark();
const { datasetList, fetchDatasetData } = useQueryDataset();
const { exportData } = useExportBenchmark();
const {
fetchClusterList,
cancelRequest: cancelClusterRequest,
clusterList
} = useQueryClusterList();
const { SettingsButton, columns: selectedColumns } = useColumnSettings({
contentHeight: 320,
clusterList
});
useEffect(() => {
fetchModelList({ page: -1 });
fetchClusterList({ page: -1 });
fetchDatasetData();
return () => {
cancelClusterRequest();
};
}, []);
const handleAddBenchmark = () => {
@@ -110,7 +120,7 @@ const Benchmark: React.FC = () => {
closeBenchmarkModal();
};
const handleEditUser = (row: ListItem) => {
const handleEdit = (row: ListItem) => {
openBenchmarkModal(
PageAction.EDIT,
intl.formatMessage({ id: 'benchmark.button.edit' }),
@@ -120,7 +130,7 @@ const Benchmark: React.FC = () => {
const handleSelect = useMemoizedFn((val: any, row: ListItem) => {
if (val === 'edit') {
handleEditUser(row);
handleEdit(row);
} else if (val === 'delete') {
handleDelete({ ...row, name: row.name });
} else if (val === 'viewlog') {
@@ -134,7 +144,8 @@ const Benchmark: React.FC = () => {
(record: ListItem, dataIndex: string) => {
if (dataIndex === 'name') {
navigate(
`/models/benchmark/detail?id=${record.id}&name=${record.name}`
`/models/benchmark/detail?id=${record.id}&name=${record.name}`,
{ state: 'benchmark-details' }
);
}
}
@@ -230,6 +241,7 @@ const Benchmark: React.FC = () => {
</ConfigProvider>
</PageBox>
<AddBenchmarkModal
clusterList={clusterList}
open={openBenchmarkModalStatus.open}
action={openBenchmarkModalStatus.action}
title={openBenchmarkModalStatus.title}
@@ -0,0 +1,25 @@
import { useQueryDataList } from '@/hooks/use-query-data-list';
import { queryBenchmarkList } from '../apis';
import { BenchmarkListItem as ListItem } from '../config/types';
export const useQueryBenchmarkList = (optons?: {
getLabel?: (item: ListItem) => string;
getValue?: (item: ListItem) => any;
}) => {
const { dataList, loading, fetchData, cancelRequest } = useQueryDataList<
ListItem,
Global.SearchParams
>({
key: 'benchmarkList',
fetchList: queryBenchmarkList
});
return {
dataList,
loading,
fetchData,
cancelRequest
};
};
export default useQueryBenchmarkList;