feat: add backend pages
This commit is contained in:
@@ -105,6 +105,16 @@ export default [
|
|||||||
defaultIcon: 'icon-models',
|
defaultIcon: 'icon-models',
|
||||||
access: 'canSeeAdmin',
|
access: 'canSeeAdmin',
|
||||||
component: './llmodels/user-models'
|
component: './llmodels/user-models'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'backendsList',
|
||||||
|
path: '/models/backends/list',
|
||||||
|
key: 'backendsList',
|
||||||
|
icon: 'icon-backend',
|
||||||
|
selectedIcon: 'icon-backend-filled',
|
||||||
|
defaultIcon: 'icon-backend',
|
||||||
|
access: 'canSeeAdmin',
|
||||||
|
component: './backends/list'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 393 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons';
|
|||||||
// import './iconfont/iconfont.js';
|
// import './iconfont/iconfont.js';
|
||||||
|
|
||||||
const IconFont = createFromIconfontCN({
|
const IconFont = createFromIconfontCN({
|
||||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_1ahssp5gmym.js'
|
scriptUrl: '//at.alicdn.com/t/c/font_4613488_wt33o3w25g.js'
|
||||||
});
|
});
|
||||||
|
|
||||||
export default IconFont;
|
export default IconFont;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ interface FilterBarProps {
|
|||||||
actionItems?: ActionItem[];
|
actionItems?: ActionItem[];
|
||||||
selectOptions?: Global.BaseOption<string | number>[];
|
selectOptions?: Global.BaseOption<string | number>[];
|
||||||
showSelect?: boolean;
|
showSelect?: boolean;
|
||||||
buttonText: string;
|
buttonText?: string;
|
||||||
buttonIcon?: React.ReactNode;
|
buttonIcon?: React.ReactNode;
|
||||||
marginBottom?: number;
|
marginBottom?: number;
|
||||||
marginTop?: number;
|
marginTop?: number;
|
||||||
@@ -72,6 +72,8 @@ interface FilterBarProps {
|
|||||||
actionType?: 'dropdown' | 'button';
|
actionType?: 'dropdown' | 'button';
|
||||||
showPrimaryButton?: boolean;
|
showPrimaryButton?: boolean;
|
||||||
showDeleteButton?: boolean;
|
showDeleteButton?: boolean;
|
||||||
|
right?: React.ReactNode;
|
||||||
|
left?: React.ReactNode;
|
||||||
width?: {
|
width?: {
|
||||||
input?: number;
|
input?: number;
|
||||||
select?: number;
|
select?: number;
|
||||||
@@ -98,6 +100,8 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
selectHolder,
|
selectHolder,
|
||||||
showPrimaryButton = true,
|
showPrimaryButton = true,
|
||||||
showDeleteButton = true,
|
showDeleteButton = true,
|
||||||
|
right,
|
||||||
|
left,
|
||||||
width
|
width
|
||||||
} = props;
|
} = props;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -195,7 +199,7 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
></Button>
|
></Button>
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
right={renderRight}
|
right={right || renderRight}
|
||||||
></PageTools>
|
></PageTools>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ const Inner = styled.div.attrs({
|
|||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: flex-start;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -31,5 +31,6 @@ export default {
|
|||||||
'menu.clusterManagement.clusters': 'Clusters',
|
'menu.clusterManagement.clusters': 'Clusters',
|
||||||
'menu.clusterManagement.credentials': 'Cloud Credentials',
|
'menu.clusterManagement.credentials': 'Cloud Credentials',
|
||||||
'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
||||||
'menu.clusterManagement.clusterCreate': 'Create Cluster'
|
'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
||||||
|
'menu.models.backendsList': 'Backends'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ export default {
|
|||||||
'menu.clusterManagement.credentials': 'Cloud Credentials',
|
'menu.clusterManagement.credentials': 'Cloud Credentials',
|
||||||
'menu.models.userModels': 'My Models',
|
'menu.models.userModels': 'My Models',
|
||||||
'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
||||||
'menu.clusterManagement.clusterCreate': 'Create Cluster'
|
'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
||||||
|
'menu.models.backendsList': 'Backends'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -47,5 +48,6 @@ export default {
|
|||||||
// 10. 'menu.clusterManagement.credentials': 'Cloud Credentials',
|
// 10. 'menu.clusterManagement.credentials': 'Cloud Credentials',
|
||||||
// 11. 'menu.models.userModels': 'My Models'
|
// 11. 'menu.models.userModels': 'My Models'
|
||||||
// 12. 'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
// 12. 'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
||||||
// 13. 'menu.clusterManagement.clusterCreate': 'Create Cluster'
|
// 13. 'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
||||||
|
// 14. 'menu.models.backendsList': 'Backends'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ export default {
|
|||||||
'menu.clusterManagement.credentials': 'Облачные аккаунты',
|
'menu.clusterManagement.credentials': 'Облачные аккаунты',
|
||||||
'menu.models.userModels': 'Мои модели',
|
'menu.models.userModels': 'Мои модели',
|
||||||
'menu.clusterManagement.clusterDetail': 'Детали кластера',
|
'menu.clusterManagement.clusterDetail': 'Детали кластера',
|
||||||
'menu.clusterManagement.clusterCreate': 'Создать кластер'
|
'menu.clusterManagement.clusterCreate': 'Создать кластер',
|
||||||
|
'menu.models.backendsList': 'Backends'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
// 14. 'menu.models.backendsList': 'Backends'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -31,5 +31,6 @@ export default {
|
|||||||
'menu.clusterManagement.clusters': '集群',
|
'menu.clusterManagement.clusters': '集群',
|
||||||
'menu.clusterManagement.credentials': '云凭证',
|
'menu.clusterManagement.credentials': '云凭证',
|
||||||
'menu.clusterManagement.clusterDetail': '集群详情',
|
'menu.clusterManagement.clusterDetail': '集群详情',
|
||||||
'menu.clusterManagement.clusterCreate': '创建集群'
|
'menu.clusterManagement.clusterCreate': '创建集群',
|
||||||
|
'menu.models.backendsList': '后端'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { request } from '@umijs/max';
|
||||||
|
import { FormData, ListItem } from '../config/types';
|
||||||
|
|
||||||
|
export const INFERENCE_BACKEND_API = '/inference-backend';
|
||||||
|
|
||||||
|
export const FROM_YAML_API = '/from-yaml';
|
||||||
|
|
||||||
|
export async function queryBackendsList(params: Global.SearchParams) {
|
||||||
|
return request<Global.PageResponse<ListItem>>(`${INFERENCE_BACKEND_API}`, {
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createBackend(params: { data: FormData }) {
|
||||||
|
return request<ListItem>(`${INFERENCE_BACKEND_API}`, {
|
||||||
|
method: 'POST',
|
||||||
|
data: params.data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateBackend(id: number, params: { data: FormData }) {
|
||||||
|
return request<ListItem>(`${INFERENCE_BACKEND_API}/${id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
data: params.data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteBackend(id: number) {
|
||||||
|
return request(`${INFERENCE_BACKEND_API}/${id}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createBackendFromYAML(params: {
|
||||||
|
data: { content: string };
|
||||||
|
}) {
|
||||||
|
return request<ListItem>(`${INFERENCE_BACKEND_API}${FROM_YAML_API}`, {
|
||||||
|
method: 'POST',
|
||||||
|
data: params.data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateBackendFromYAML(
|
||||||
|
id: number,
|
||||||
|
params: { data: { content: string } }
|
||||||
|
) {
|
||||||
|
return request<ListItem>(`${INFERENCE_BACKEND_API}/${id}${FROM_YAML_API}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
data: params.data
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import DropDownActions from '@/components/drop-down-actions';
|
||||||
|
import IconFont from '@/components/icon-font';
|
||||||
|
import Card from '@/components/templates/card';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { backendActions } from '../config';
|
||||||
|
import { ListItem } from '../config/types';
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Content = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-top: 12x;
|
||||||
|
gap: 16px;
|
||||||
|
.title {
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface BackendCardProps {
|
||||||
|
onClick: (data: any) => void;
|
||||||
|
onSelect?: (item: any) => void;
|
||||||
|
data: ListItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BackendCard: React.FC<BackendCardProps> = ({
|
||||||
|
onClick,
|
||||||
|
data,
|
||||||
|
onSelect
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
onClick={() => onClick(data)}
|
||||||
|
clickable={true}
|
||||||
|
disabled={false}
|
||||||
|
height={205}
|
||||||
|
ghost
|
||||||
|
header={
|
||||||
|
<Header>
|
||||||
|
<div className="title">
|
||||||
|
<img src={data.icon} alt={data.backend_name} height={20} />
|
||||||
|
</div>
|
||||||
|
<span>
|
||||||
|
<DropDownActions
|
||||||
|
menu={{
|
||||||
|
items: backendActions,
|
||||||
|
onClick: onSelect
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon={<IconFont type="icon-more"></IconFont>}
|
||||||
|
size="small"
|
||||||
|
type="text"
|
||||||
|
></Button>
|
||||||
|
</DropDownActions>
|
||||||
|
</span>
|
||||||
|
</Header>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Content>
|
||||||
|
<div className="title">{data.backend_name}</div>
|
||||||
|
<div>This is a description</div>
|
||||||
|
<div>默认版本:{data.default_version}</div>
|
||||||
|
<div>
|
||||||
|
镜像:{data.version_configs?.[data.default_version]?.image_name}
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BackendCard;
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import CardSkeleton from '@/components/templates/card-skelton';
|
||||||
|
import breakpoints from '@/config/breakpoints';
|
||||||
|
import { Col, FloatButton, Row, Spin } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import ResizeObserver from 'rc-resize-observer';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import BackendCard from './backend-card';
|
||||||
|
|
||||||
|
const SpinWrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
max-height: 400px;
|
||||||
|
right: 0;
|
||||||
|
.skelton-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface BackendListProps {
|
||||||
|
defaultSpan?: number;
|
||||||
|
resizable?: boolean;
|
||||||
|
dataList: any[];
|
||||||
|
loading: boolean;
|
||||||
|
activeId: Global.WithFalse<number>;
|
||||||
|
isFirst: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InnerSkeleton: React.FC<{
|
||||||
|
span: number;
|
||||||
|
}> = (props) => {
|
||||||
|
return <CardSkeleton {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ListSkeleton: React.FC<{
|
||||||
|
span: number;
|
||||||
|
loading: boolean;
|
||||||
|
isFirst: boolean;
|
||||||
|
}> = ({ span, loading, isFirst }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{loading && (
|
||||||
|
<SpinWrapper>
|
||||||
|
<Spin
|
||||||
|
spinning={loading}
|
||||||
|
style={{
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
wrapperClassName="skelton-wrapper"
|
||||||
|
>
|
||||||
|
{isFirst && <InnerSkeleton span={span}></InnerSkeleton>}
|
||||||
|
</Spin>
|
||||||
|
</SpinWrapper>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CardList: React.FC<BackendListProps> = (props) => {
|
||||||
|
const {
|
||||||
|
dataList,
|
||||||
|
loading,
|
||||||
|
isFirst,
|
||||||
|
defaultSpan = 6,
|
||||||
|
resizable = true
|
||||||
|
} = props;
|
||||||
|
const [span, setSpan] = React.useState(defaultSpan);
|
||||||
|
|
||||||
|
const getSpanByWidth = (width: number) => {
|
||||||
|
if (width < breakpoints.md) return 24;
|
||||||
|
if (width < breakpoints.lg) return 12;
|
||||||
|
return 6;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResize = useCallback(
|
||||||
|
_.throttle((size: { width: number; height: number }) => {
|
||||||
|
setSpan(getSpanByWidth(size.width));
|
||||||
|
}, 100),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative" style={{ width: '100%' }}>
|
||||||
|
<ResizeObserver onResize={handleResize} disabled={!resizable}>
|
||||||
|
<div style={{ width: '100%' }}>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
{dataList.map((item: any) => {
|
||||||
|
return (
|
||||||
|
<Col span={span} key={item.id}>
|
||||||
|
<BackendCard onClick={() => {}} data={item} />
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
<ListSkeleton span={span} loading={loading} isFirst={isFirst} />
|
||||||
|
</div>
|
||||||
|
</ResizeObserver>
|
||||||
|
<FloatButton.BackTop visibilityHeight={1000} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CardList;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import icons from '@/components/icon-font/icons';
|
||||||
|
|
||||||
|
export const backendActions = [
|
||||||
|
{
|
||||||
|
label: 'Edit Versions',
|
||||||
|
value: 'version',
|
||||||
|
key: 'version',
|
||||||
|
icon: icons.EditOutlined,
|
||||||
|
locale: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Edit Parameters',
|
||||||
|
value: 'parameter',
|
||||||
|
key: 'parameter',
|
||||||
|
icon: icons.EditOutlined,
|
||||||
|
locale: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Edit by Yaml',
|
||||||
|
value: 'yaml',
|
||||||
|
key: 'edit',
|
||||||
|
icon: icons.EditOutlined,
|
||||||
|
locale: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
key: 'delete',
|
||||||
|
icon: icons.DeleteOutlined,
|
||||||
|
locale: false,
|
||||||
|
danger: true
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import nvidiaLogo from '@/assets/logo/nvidia.png';
|
||||||
|
import SGLangLogo from '@/assets/logo/sglang.png';
|
||||||
|
import vLLMLogo from '@/assets/logo/vllm.png';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
backend_name: 'SGlang',
|
||||||
|
icon: SGLangLogo,
|
||||||
|
version_configs: {
|
||||||
|
'v1.0': {
|
||||||
|
image_name: 'example/image:v1.0',
|
||||||
|
run_command: 'run.sh'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
compatibility_type: 'template_string',
|
||||||
|
health_check_path: '/v1/models',
|
||||||
|
default_version: 'v1.0',
|
||||||
|
default_backend_param: ['--param1', '--param2'],
|
||||||
|
description: 'sglang description'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
backend_name: 'vLLM',
|
||||||
|
icon: vLLMLogo,
|
||||||
|
version_configs: {
|
||||||
|
'v1.0': {
|
||||||
|
image_name: 'example/image:v1.0',
|
||||||
|
run_command: 'run.sh'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
compatibility_type: 'template_string',
|
||||||
|
health_check_path: '/v1/models',
|
||||||
|
default_version: 'v1.0',
|
||||||
|
default_backend_param: ['--param1', '--param2'],
|
||||||
|
description: 'vllm description'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
backend_name: 'TensorRT',
|
||||||
|
icon: nvidiaLogo,
|
||||||
|
version_configs: {
|
||||||
|
'v1.0': {
|
||||||
|
image_name: 'example/image:v1.0',
|
||||||
|
run_command: 'run.sh'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
compatibility_type: 'template_string',
|
||||||
|
health_check_path: '/v1/models',
|
||||||
|
default_version: 'v1.0',
|
||||||
|
default_backend_param: ['--param1', '--param2'],
|
||||||
|
description: 'TensorRT description'
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export interface VersionConfigs {
|
||||||
|
[key: string]: {
|
||||||
|
image_name: string;
|
||||||
|
run_command: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormData {
|
||||||
|
backend_name: string;
|
||||||
|
description?: string;
|
||||||
|
version_configs?: VersionConfigs;
|
||||||
|
compatibility_type?: string;
|
||||||
|
health_check_path?: string;
|
||||||
|
default_version?: string;
|
||||||
|
default_backend_param?: string[];
|
||||||
|
allowed_proxy_uris?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ListItem extends FormData {
|
||||||
|
id: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import { FilterBar } from '@/components/page-tools';
|
||||||
|
import useTableFetch from '@/hooks/use-table-fetch';
|
||||||
|
import {
|
||||||
|
DeleteOutlined,
|
||||||
|
ImportOutlined,
|
||||||
|
PlusOutlined
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Button, Space } from 'antd';
|
||||||
|
import {
|
||||||
|
deleteBackend,
|
||||||
|
INFERENCE_BACKEND_API,
|
||||||
|
queryBackendsList
|
||||||
|
} from './apis';
|
||||||
|
import BackendCardList from './components/backend-list';
|
||||||
|
import dataList from './config/test';
|
||||||
|
import { ListItem } from './config/types';
|
||||||
|
|
||||||
|
const BackendList = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const {
|
||||||
|
dataSource,
|
||||||
|
rowSelection,
|
||||||
|
queryParams,
|
||||||
|
modalRef,
|
||||||
|
fetchData,
|
||||||
|
handleDelete,
|
||||||
|
handleDeleteBatch,
|
||||||
|
handlePageChange,
|
||||||
|
handleTableChange,
|
||||||
|
handleSearch,
|
||||||
|
handleNameChange,
|
||||||
|
handleQueryChange
|
||||||
|
} = useTableFetch<ListItem>({
|
||||||
|
fetchAPI: queryBackendsList,
|
||||||
|
deleteAPI: deleteBackend,
|
||||||
|
API: INFERENCE_BACKEND_API,
|
||||||
|
watch: false,
|
||||||
|
contentForDelete: 'resources.modelfiles.modelfile'
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClickDropdown = () => {};
|
||||||
|
|
||||||
|
const handleDeleteByBatch = () => {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer
|
||||||
|
ghost
|
||||||
|
header={{
|
||||||
|
title: intl.formatMessage({ id: 'menu.models.backendsList' }),
|
||||||
|
style: {
|
||||||
|
paddingInline: 'var(--layout-content-header-inlinepadding)'
|
||||||
|
},
|
||||||
|
breadcrumb: {}
|
||||||
|
}}
|
||||||
|
extra={[]}
|
||||||
|
>
|
||||||
|
<FilterBar
|
||||||
|
marginBottom={22}
|
||||||
|
marginTop={30}
|
||||||
|
width={{
|
||||||
|
input: 300
|
||||||
|
}}
|
||||||
|
actionType="dropdown"
|
||||||
|
inputHolder={intl.formatMessage({ id: 'common.filter.name' })}
|
||||||
|
buttonText={intl.formatMessage({
|
||||||
|
id: 'resources.modelfiles.download'
|
||||||
|
})}
|
||||||
|
handleDeleteByBatch={handleDeleteByBatch}
|
||||||
|
handleClickPrimary={handleClickDropdown}
|
||||||
|
handleSearch={handleSearch}
|
||||||
|
handleInputChange={handleNameChange}
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
showSelect={false}
|
||||||
|
right={
|
||||||
|
<Space size={20}>
|
||||||
|
<Button type="primary" icon={<PlusOutlined />}>
|
||||||
|
Add Backend
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" icon={<ImportOutlined />}>
|
||||||
|
Import backend from yaml
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
icon={<DeleteOutlined />}
|
||||||
|
danger
|
||||||
|
onClick={handleDeleteByBatch}
|
||||||
|
disabled={!rowSelection?.selectedRowKeys?.length}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{intl?.formatMessage?.({ id: 'common.button.delete' })}
|
||||||
|
{rowSelection?.selectedRowKeys?.length > 0 && (
|
||||||
|
<span>({rowSelection?.selectedRowKeys?.length})</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
></FilterBar>
|
||||||
|
|
||||||
|
<BackendCardList
|
||||||
|
dataList={dataList}
|
||||||
|
loading={dataSource.loading}
|
||||||
|
activeId={false}
|
||||||
|
isFirst={!dataSource.loadend}
|
||||||
|
></BackendCardList>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BackendList;
|
||||||
@@ -12,6 +12,9 @@ const Wrapper = styled.div<{ $cols?: number }>`
|
|||||||
.template-card-wrapper {
|
.template-card-wrapper {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
.template-card-inner {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
|
|||||||
Reference in New Issue
Block a user