refactor: models list table columns from props

This commit is contained in:
jialin
2025-02-19 15:17:17 +08:00
parent a9006f2954
commit 9e0634f449
12 changed files with 372 additions and 345 deletions
+19 -22
View File
@@ -4,16 +4,16 @@ import { SealColumnProps } from '../types';
import TableHeader from './table-header'; import TableHeader from './table-header';
interface HeaderProps { interface HeaderProps {
children: React.ReactNode[]; columns: SealColumnProps[];
onSort?: (dataIndex: string, order: any) => void; onSort?: (dataIndex: string, order: any) => void;
} }
const Header: React.FC<HeaderProps> = (props) => { const Header: React.FC<HeaderProps> = (props) => {
const { onSort } = props; const { onSort } = props;
return ( return (
<Row className="row"> <Row className="row">
{React.Children.map(props.children, (child, i) => { {props.columns?.map((columnProps, i) => {
const { props: columnProps } = child as any;
const { const {
title, title,
dataIndex, dataIndex,
@@ -24,25 +24,22 @@ const Header: React.FC<HeaderProps> = (props) => {
sorter, sorter,
defaultSortOrder defaultSortOrder
} = columnProps as SealColumnProps; } = columnProps as SealColumnProps;
if (React.isValidElement(child)) { return (
return ( <Col span={span} key={dataIndex || i}>
<Col span={span} key={i}> <TableHeader
<TableHeader onSort={onSort}
onSort={onSort} sorter={sorter}
sorter={sorter} dataIndex={dataIndex}
dataIndex={dataIndex} sortOrder={sortOrder}
sortOrder={sortOrder} defaultSortOrder={defaultSortOrder}
defaultSortOrder={defaultSortOrder} title={title}
title={title} style={headerStyle}
style={headerStyle} firstCell={i === 0}
firstCell={i === 0} align={align}
align={align} lastCell={i === props.columns.length - 1}
lastCell={i === props.children.length - 1} ></TableHeader>
></TableHeader> </Col>
</Col> );
);
}
return null;
})} })}
</Row> </Row>
); );
@@ -0,0 +1,64 @@
import { DownOutlined, RightOutlined } from '@ant-design/icons';
import { Button, Checkbox } from 'antd';
import _ from 'lodash';
import React from 'react';
interface RowPrefixProps {
expandable?: boolean | React.ReactNode;
enableSelection?: boolean;
expanded?: boolean;
checked?: boolean;
handleRowExpand?: () => void;
handleSelectChange?: (e: any) => void;
}
const RowPrefix: React.FC<RowPrefixProps> = (props) => {
const {
expandable,
enableSelection,
expanded,
checked,
handleRowExpand,
handleSelectChange
} = props;
if (expandable && enableSelection) {
return (
<div className="row-prefix-wrapper">
<span style={{ marginRight: 5 }}>
{_.isBoolean(expandable) ? (
<Button type="text" size="small" onClick={handleRowExpand}>
{expanded ? <DownOutlined /> : <RightOutlined />}
</Button>
) : (
expandable
)}
</span>
<Checkbox onChange={handleSelectChange} checked={checked}></Checkbox>
</div>
);
}
if (expandable) {
return (
<div className="row-prefix-wrapper">
{_.isBoolean(expandable) ? (
<Button type="text" size="small" onClick={handleRowExpand}>
{expanded ? <DownOutlined /> : <RightOutlined />}
</Button>
) : (
expandable
)}
</div>
);
}
if (enableSelection) {
return (
<div className="row-prefix-wrapper">
{<Checkbox onChange={handleSelectChange} checked={checked}></Checkbox>}
</div>
);
}
return null;
};
export default RowPrefix;
@@ -1,13 +0,0 @@
import { Checkbox } from 'antd';
import '../styles/skeleton.less';
const RowSkeleton = () => {
return (
<div className="row-skeleton">
<div className="holder"></div>
<Checkbox></Checkbox>
</div>
);
};
export default RowSkeleton;
@@ -1,10 +1,11 @@
import { Empty } from 'antd'; import { Empty } from 'antd';
import React from 'react'; import React from 'react';
import { SealColumnProps } from '../types';
import TableRow from './table-row'; import TableRow from './table-row';
interface TableBodyProps { interface TableBodyProps {
dataSource: any[]; dataSource: any[];
columns: React.ReactNode[]; columns: SealColumnProps[];
rowKey: string; rowKey: string;
rowSelection?: any; rowSelection?: any;
expandable?: any; expandable?: any;
@@ -2,14 +2,21 @@ import useSetChunkRequest, {
createAxiosToken createAxiosToken
} from '@/hooks/use-chunk-request'; } from '@/hooks/use-chunk-request';
import useUpdateChunkedList from '@/hooks/use-update-chunk-list'; import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
import { DownOutlined, RightOutlined } from '@ant-design/icons'; import { Col, Empty, Row, Spin } from 'antd';
import { Button, Checkbox, Col, Empty, Row, Spin } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import _ from 'lodash'; import _ from 'lodash';
import React, { useEffect, useRef, useState } from 'react'; import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react';
import RowContext from '../row-context'; import RowContext from '../row-context';
import TableContext from '../table-context'; import TableContext from '../table-context';
import { RowContextProps, SealTableProps } from '../types'; import { RowContextProps, SealTableProps } from '../types';
import RowPrefix from './row-prefix';
import TableColumn from './seal-column';
const TableRow: React.FC< const TableRow: React.FC<
RowContextProps & RowContextProps &
@@ -38,7 +45,7 @@ const TableRow: React.FC<
}>(TableContext); }>(TableContext);
const { setChunkRequest } = useSetChunkRequest(); const { setChunkRequest } = useSetChunkRequest();
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
const [checked, setChecked] = useState(false); // const [checked, setChecked] = useState(false);
const [childrenData, setChildrenData] = useState<any[]>([]); const [childrenData, setChildrenData] = useState<any[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [firstLoad, setFirstLoad] = useState(true); const [firstLoad, setFirstLoad] = useState(true);
@@ -56,17 +63,6 @@ const TableRow: React.FC<
// callback: (list) => renderChildren?.(list) // callback: (list) => renderChildren?.(list)
}); });
useEffect(() => {
if (rowSelection) {
const { selectedRowKeys } = rowSelection;
if (selectedRowKeys.includes(record[rowKey])) {
setChecked(true);
} else {
setChecked(false);
}
}
}, [rowSelection]);
useEffect(() => { useEffect(() => {
return () => { return () => {
if (pollTimer.current) { if (pollTimer.current) {
@@ -77,7 +73,23 @@ const TableRow: React.FC<
}; };
}, []); }, []);
const checked = useMemo(() => {
return rowSelection?.selectedRowKeys?.includes(record[rowKey]);
}, [rowSelection?.selectedRowKeys, record, rowKey]);
const renderChildrenData = () => { const renderChildrenData = () => {
if (childrenData.length === 0) {
return (
<Empty
image={loading ? null : Empty.PRESENTED_IMAGE_SIMPLE}
description={loading ? null : undefined}
style={{
marginBlock: 0,
height: 54
}}
></Empty>
);
}
return renderChildren?.(childrenData, record); return renderChildren?.(childrenData, record);
}; };
@@ -92,7 +104,7 @@ const TableRow: React.FC<
} }
}; };
const handleLoadChildren = async () => { const handleLoadChildren = useCallback(async () => {
try { try {
axiosToken.current?.cancel?.(); axiosToken.current?.cancel?.();
axiosToken.current = createAxiosToken(); axiosToken.current = createAxiosToken();
@@ -109,7 +121,7 @@ const TableRow: React.FC<
} finally { } finally {
setFirstLoad(false); setFirstLoad(false);
} }
}; }, [record, loadChildren]);
const filterUpdateChildrenHandler = () => { const filterUpdateChildrenHandler = () => {
if (!expandedRowKeys?.includes(record[rowKey])) { if (!expandedRowKeys?.includes(record[rowKey])) {
@@ -224,51 +236,6 @@ const TableRow: React.FC<
}; };
}, [updateChild, tableContext.allChildren]); }, [updateChild, tableContext.allChildren]);
const renderRowPrefix = () => {
if (expandable && rowSelection) {
return (
<div className="row-prefix-wrapper">
<span style={{ marginRight: 5 }}>
{_.isBoolean(expandable) ? (
<Button type="text" size="small" onClick={handleRowExpand}>
{expanded ? <DownOutlined /> : <RightOutlined />}
</Button>
) : (
expandable
)}
</span>
<Checkbox onChange={handleSelectChange} checked={checked}></Checkbox>
</div>
);
}
if (expandable) {
return (
<div className="row-prefix-wrapper">
{_.isBoolean(expandable) ? (
<Button type="text" size="small" onClick={handleRowExpand}>
{expanded ? <DownOutlined /> : <RightOutlined />}
</Button>
) : (
expandable
)}
</div>
);
}
if (rowSelection) {
return (
<div className="row-prefix-wrapper">
{
<Checkbox
onChange={handleSelectChange}
checked={checked}
></Checkbox>
}
</div>
);
}
return null;
};
return ( return (
<RowContext.Provider value={{ row: { ...record, rowIndex }, onCell }}> <RowContext.Provider value={{ row: { ...record, rowIndex }, onCell }}>
<div className="row-box"> <div className="row-box">
@@ -277,40 +244,30 @@ const TableRow: React.FC<
'row-wrapper-selected': checked 'row-wrapper-selected': checked
})} })}
> >
{renderRowPrefix()} <RowPrefix
expandable={expandable}
enableSelection={rowSelection?.enableSelection}
expanded={expanded}
checked={checked}
handleRowExpand={handleRowExpand}
handleSelectChange={handleSelectChange}
></RowPrefix>
<Row className="seal-table-row"> <Row className="seal-table-row">
{React.Children.map(columns, (child) => { {columns?.map((columnProps) => {
const { props: columnProps } = child as any; return (
if (React.isValidElement(child)) { <Col
return ( key={`${columnProps.dataIndex}-${rowIndex}`}
<Col span={columnProps.span}
key={`${columnProps.dataIndex}-${rowIndex}`} >
span={columnProps.span} <TableColumn {...columnProps}></TableColumn>
> </Col>
{child} );
</Col>
);
}
return <Col span={columnProps.span}></Col>;
})} })}
</Row> </Row>
</div> </div>
{expanded && ( {expanded && (
<div className="expanded-row"> <div className="expanded-row">
<Spin spinning={loading}> <Spin spinning={loading}>{renderChildrenData()}</Spin>
{childrenData.length ? (
renderChildrenData()
) : (
<Empty
image={loading ? null : Empty.PRESENTED_IMAGE_SIMPLE}
description={loading ? null : undefined}
style={{
marginBlock: 0,
height: 54
}}
></Empty>
)}
</Spin>
</div> </div>
)} )}
</div> </div>
@@ -0,0 +1,32 @@
import { Checkbox } from 'antd';
import React from 'react';
import styled from 'styled-components';
import '../styles/skeleton.less';
const SkeletonItem = () => {
return (
<div className="row-skeleton">
<div className="holder"></div>
<Checkbox></Checkbox>
</div>
);
};
const Wrapper = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
`;
const TableSkeleton = () => {
const dataSource = Array.from({ length: 5 });
return (
<Wrapper>
{dataSource.map((item, index) => (
<SkeletonItem key={index} />
))}
</Wrapper>
);
};
export default React.memo(TableSkeleton);
+43 -29
View File
@@ -1,16 +1,17 @@
import { Pagination, Spin, type PaginationProps } from 'antd'; import { Pagination, Spin, type PaginationProps } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import React, { useEffect, useState } from 'react'; import React, { useMemo } from 'react';
import Header from './components/header'; import Header from './components/header';
import HeaderPrefix from './components/header-prefix'; import HeaderPrefix from './components/header-prefix';
import TableBody from './components/table-body'; import TableBody from './components/table-body';
import './styles/index.less'; import './styles/index.less';
import { SealTableProps } from './types'; import { SealColumnProps, SealTableProps } from './types';
const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = ( const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
props props
) => { ) => {
const { const {
columns,
children, children,
rowKey, rowKey,
childParentKey, childParentKey,
@@ -29,33 +30,46 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
loadChildren, loadChildren,
loadChildrenAPI loadChildrenAPI
} = props; } = props;
const [selectState, setSelectState] = useState({
selectAll: false,
indeterminate: false
});
useEffect(() => { const parsedColumns = useMemo(() => {
if (rowSelection) { if (columns) return columns;
const { selectedRowKeys } = rowSelection;
const selectedKeys = new Set(rowSelection.selectedRowKeys); return React.Children.toArray(children)
const allRowKeys = props.dataSource.map((record) => record[rowKey]); .filter(React.isValidElement)
if (!selectedRowKeys?.length) { .map((child) => {
setSelectState({ const column = child as React.ReactElement<SealColumnProps>;
selectAll: false, const { title, dataIndex, key, render, ...restProps } = column.props;
indeterminate: false
}); return {
} else if (allRowKeys.every((key) => selectedKeys.has(key))) { title,
setSelectState({ dataIndex,
selectAll: true, key: key || dataIndex,
indeterminate: false render,
}); ...restProps
} else { };
setSelectState({ });
selectAll: false, }, [columns, children]);
indeterminate: true
}); const selectState = useMemo(() => {
} const selectedRowKeys = rowSelection?.selectedRowKeys || [];
const selectedKeys = new Set(selectedRowKeys);
const allRowKeys = props.dataSource.map((record) => record[rowKey]);
if (!selectedRowKeys?.length) {
return {
selectAll: false,
indeterminate: false
};
} }
if (allRowKeys.every((key) => selectedKeys.has(key))) {
return {
selectAll: true,
indeterminate: false
};
}
return {
selectAll: false,
indeterminate: true
};
}, [rowSelection?.selectedRowKeys, props.dataSource, rowKey]); }, [rowSelection?.selectedRowKeys, props.dataSource, rowKey]);
const selectAllRows = () => { const selectAllRows = () => {
@@ -103,12 +117,12 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
expandable={expandable} expandable={expandable}
enableSelection={rowSelection?.enableSelection} enableSelection={rowSelection?.enableSelection}
></HeaderPrefix> ></HeaderPrefix>
<Header onSort={onSort}>{children}</Header> <Header onSort={onSort} columns={parsedColumns}></Header>
</div> </div>
<Spin spinning={loading}> <Spin spinning={loading}>
<TableBody <TableBody
dataSource={props.dataSource} dataSource={props.dataSource}
columns={children} columns={parsedColumns}
rowSelection={rowSelection} rowSelection={rowSelection}
expandable={expandable} expandable={expandable}
rowKey={rowKey} rowKey={rowKey}
+5 -3
View File
@@ -4,6 +4,7 @@ export interface SealColumnProps {
title: React.ReactNode; title: React.ReactNode;
render?: (text: any, record: any) => React.ReactNode; render?: (text: any, record: any) => React.ReactNode;
dataIndex: string; dataIndex: string;
key?: string;
width?: number; width?: number;
span: number; span: number;
align?: 'left' | 'center' | 'right'; align?: 'left' | 'center' | 'right';
@@ -18,6 +19,7 @@ export interface SealColumnProps {
}; };
valueType?: 'text' | 'number' | 'date' | 'datetime' | 'time'; valueType?: 'text' | 'number' | 'date' | 'datetime' | 'time';
sortOrder?: 'ascend' | 'descend' | null; sortOrder?: 'ascend' | 'descend' | null;
[key: string]: any;
} }
export interface TableHeaderProps { export interface TableHeaderProps {
@@ -26,7 +28,7 @@ export interface TableHeaderProps {
sortOrder?: 'ascend' | 'descend' | null; sortOrder?: 'ascend' | 'descend' | null;
dataIndex: string; dataIndex: string;
onSort?: (dataIndex: string, order: 'ascend' | 'descend') => void; onSort?: (dataIndex: string, order: 'ascend' | 'descend') => void;
children?: React.ReactNode; children?: React.ReactElement<SealColumnProps>;
title: React.ReactNode; title: React.ReactNode;
style?: React.CSSProperties; style?: React.CSSProperties;
firstCell?: boolean; firstCell?: boolean;
@@ -42,10 +44,11 @@ export interface RowSelectionProps {
onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => void; onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => void;
} }
export interface SealTableProps { export interface SealTableProps {
columns?: SealColumnProps[];
childParentKey?: string; childParentKey?: string;
expandedRowKeys?: React.Key[]; expandedRowKeys?: React.Key[];
rowSelection?: RowSelectionProps; rowSelection?: RowSelectionProps;
children: React.ReactNode[]; children?: React.ReactElement<SealColumnProps>[];
empty?: React.ReactNode; empty?: React.ReactNode;
expandable?: React.ReactNode; expandable?: React.ReactNode;
dataSource: any[]; dataSource: any[];
@@ -65,7 +68,6 @@ export interface SealTableProps {
export interface RowContextProps { export interface RowContextProps {
record: Record<string, any>; record: Record<string, any>;
columns: React.ReactNode[];
pollingChildren?: boolean; pollingChildren?: boolean;
rowIndex: number; rowIndex: number;
} }
+33 -51
View File
@@ -18,7 +18,7 @@ import { useIntl } from '@umijs/max';
import { Button, Col, Divider, Row, Space, Tag, Tooltip } from 'antd'; import { Button, Col, Divider, Row, Space, Tag, Tooltip } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import React, { useCallback } from 'react'; import React from 'react';
import { InstanceStatusMap, InstanceStatusMapValue, status } from '../config'; import { InstanceStatusMap, InstanceStatusMapValue, status } from '../config';
import { ModelInstanceListItem } from '../config/types'; import { ModelInstanceListItem } from '../config/types';
import '../style/instance-item.less'; import '../style/instance-item.less';
@@ -35,6 +35,38 @@ interface InstanceItemProps {
) => void; ) => void;
} }
const childActionList = [
{
label: 'common.button.viewlog',
key: 'viewlog',
status: [
InstanceStatusMap.Initializing,
InstanceStatusMap.Running,
InstanceStatusMap.Error,
InstanceStatusMap.Starting,
InstanceStatusMap.Downloading
],
icon: <IconFont type="icon-logs" />
},
{
label: 'common.button.delrecreate',
key: 'delete',
props: {
danger: true
},
icon: <DeleteOutlined />
}
];
const setChildActionList = (item: ModelInstanceListItem) => {
return _.filter(childActionList, (action: any) => {
if (action.key === 'viewlog') {
return action.status.includes(item.state);
}
return true;
});
};
const InstanceItem: React.FC<InstanceItemProps> = ({ const InstanceItem: React.FC<InstanceItemProps> = ({
list, list,
workerList, workerList,
@@ -43,56 +75,6 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const distributeCols = [
{
title: 'Worker',
key: 'worker_name'
},
{
title: 'IP',
key: 'worker_ip',
render: ({ row }: { row: ModelInstanceListItem }) => {
return row.port ? `${row.worker_ip}:${row.port}` : row.worker_ip;
}
},
{
title: intl.formatMessage({ id: 'models.table.gpuindex' }),
key: 'gpu_index'
}
];
const childActionList = [
{
label: 'common.button.viewlog',
key: 'viewlog',
status: [
InstanceStatusMap.Initializing,
InstanceStatusMap.Running,
InstanceStatusMap.Error,
InstanceStatusMap.Starting,
InstanceStatusMap.Downloading
],
icon: <IconFont type="icon-logs" />
},
{
label: 'common.button.delrecreate',
key: 'delete',
props: {
danger: true
},
icon: <DeleteOutlined />
}
];
const setChildActionList = useCallback((item: ModelInstanceListItem) => {
return _.filter(childActionList, (action: any) => {
if (action.key === 'viewlog') {
return action.status.includes(item.state);
}
return true;
});
}, []);
const renderWorkerInfo = (item: ModelInstanceListItem) => { const renderWorkerInfo = (item: ModelInstanceListItem) => {
let workerIp = '-'; let workerIp = '-';
if (item.worker_ip) { if (item.worker_ip) {
+124 -135
View File
@@ -6,7 +6,7 @@ import IconFont from '@/components/icon-font';
import { PageSize } from '@/components/logs-viewer/config'; import { PageSize } from '@/components/logs-viewer/config';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import SealTable from '@/components/seal-table'; import SealTable from '@/components/seal-table';
import SealColumn from '@/components/seal-table/components/seal-column'; import { SealColumnProps } from '@/components/seal-table/types';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import HotKeys from '@/config/hotkeys'; import HotKeys from '@/config/hotkeys';
import useBodyScroll from '@/hooks/use-body-scroll'; import useBodyScroll from '@/hooks/use-body-scroll';
@@ -32,7 +32,7 @@ import { Button, Dropdown, Input, Select, Space, Tooltip, message } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import _ from 'lodash'; import _ from 'lodash';
import { memo, useCallback, useEffect, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { import {
MODELS_API, MODELS_API,
@@ -113,6 +113,39 @@ const ActionList = [
} }
]; ];
const generateSource = (record: ListItem) => {
if (record.source === modelSourceMap.modelscope_value) {
return `${modelSourceMap.modelScope}/${record.model_scope_model_id}`;
}
if (record.source === modelSourceMap.huggingface_value) {
return `${modelSourceMap.huggingface}/${record.huggingface_repo_id}`;
}
if (record.source === modelSourceMap.local_path_value) {
return `${record.local_path}`;
}
if (record.source === modelSourceMap.ollama_library_value) {
return `${modelSourceMap.ollama_library}/${record.ollama_library_model_name}`;
}
return '';
};
const setActionList = (record: ListItem) => {
return _.filter(ActionList, (action: any) => {
if (action.key === 'chat') {
return record.ready_replicas > 0;
}
if (action.key === 'start') {
return record.replicas === 0;
}
if (action.key === 'stop') {
return record.replicas > 0;
}
return true;
});
};
const Models: React.FC<ModelsProps> = ({ const Models: React.FC<ModelsProps> = ({
handleNameChange, handleNameChange,
handleSearch, handleSearch,
@@ -270,28 +303,11 @@ const Models: React.FC<ModelsProps> = ({
} }
]; ];
const setActionList = useCallback((record: ListItem) => {
return _.filter(ActionList, (action: any) => {
if (action.key === 'chat') {
return record.ready_replicas > 0;
}
if (action.key === 'start') {
return record.replicas === 0;
}
if (action.key === 'stop') {
return record.replicas > 0;
}
return true;
});
}, []);
const handleOnSort = (dataIndex: string, order: any) => { const handleOnSort = (dataIndex: string, order: any) => {
setSortOrder(order); setSortOrder(order);
}; };
const handleOnCell = async (record: any, dataIndex: string) => { const handleOnCell = useCallback(async (record: any, dataIndex: string) => {
const params = { const params = {
id: record.id, id: record.id,
data: _.omit(record, [ data: _.omit(record, [
@@ -304,7 +320,7 @@ const Models: React.FC<ModelsProps> = ({
}; };
await updateModel(params); await updateModel(params);
message.success(intl.formatMessage({ id: 'common.message.success' })); message.success(intl.formatMessage({ id: 'common.message.success' }));
}; }, []);
const handleStartModel = async (row: ListItem) => { const handleStartModel = async (row: ListItem) => {
try { try {
@@ -502,7 +518,7 @@ const Models: React.FC<ModelsProps> = ({
[deleteModelInstance] [deleteModelInstance]
); );
const getModelInstances = async (row: any, options?: any) => { const getModelInstances = useCallback(async (row: any, options?: any) => {
try { try {
const params = { const params = {
id: row.id, id: row.id,
@@ -516,11 +532,11 @@ const Models: React.FC<ModelsProps> = ({
} catch (error) { } catch (error) {
return []; return [];
} }
}; }, []);
const generateChildrenRequestAPI = (params: any) => { const generateChildrenRequestAPI = useCallback((params: any) => {
return `${MODELS_API}/${params.id}/instances`; return `${MODELS_API}/${params.id}/instances`;
}; }, []);
const handleEdit = (row: ListItem) => { const handleEdit = (row: ListItem) => {
setCurrentData(row); setCurrentData(row);
@@ -588,22 +604,6 @@ const Models: React.FC<ModelsProps> = ({
[workerList] [workerList]
); );
const generateSource = useCallback((record: ListItem) => {
if (record.source === modelSourceMap.modelscope_value) {
return `${modelSourceMap.modelScope}/${record.model_scope_model_id}`;
}
if (record.source === modelSourceMap.huggingface_value) {
return `${modelSourceMap.huggingface}/${record.huggingface_repo_id}`;
}
if (record.source === modelSourceMap.local_path_value) {
return `${record.local_path}`;
}
if (record.source === modelSourceMap.ollama_library_value) {
return `${modelSourceMap.ollama_library}/${record.ollama_library_model_name}`;
}
return '';
}, []);
const handleClickDropdown = (item: any) => { const handleClickDropdown = (item: any) => {
if (item.key === 'huggingface') { if (item.key === 'huggingface') {
setOpenDeployModal({ setOpenDeployModal({
@@ -667,6 +667,87 @@ const Models: React.FC<ModelsProps> = ({
}); });
}; };
const columns: SealColumnProps[] = useMemo(
() => [
{
title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'name',
key: 'name',
width: 400,
span: 5,
render: (text: string, record: ListItem) => (
<span className="flex-center" style={{ maxWidth: '100%' }}>
<AutoTooltip ghost>
<span className="m-r-5">{text}</span>
</AutoTooltip>
<ModelTag categoryKey={record.categories?.[0] || ''} />
</span>
)
},
{
title: intl.formatMessage({ id: 'models.form.source' }),
dataIndex: 'source',
key: 'source',
span: 6,
render: (text: string, record: ListItem) => (
<span className="flex flex-column" style={{ width: '100%' }}>
<AutoTooltip ghost>{generateSource(record)}</AutoTooltip>
</span>
)
},
{
title: (
<Tooltip
title={intl.formatMessage({ id: 'models.form.replicas.tips' })}
>
{intl.formatMessage({ id: 'models.form.replicas' })}
<QuestionCircleOutlined className="m-l-5" />
</Tooltip>
),
dataIndex: 'replicas',
key: 'replicas',
align: 'center',
span: 4,
editable: {
valueType: 'number',
title: intl.formatMessage({ id: 'models.table.replicas.edit' })
},
render: (text: number, record: ListItem) => (
<span style={{ paddingLeft: 10, minWidth: '33px' }}>
{record.ready_replicas} / {record.replicas}
</span>
)
},
{
title: intl.formatMessage({ id: 'common.table.createTime' }),
dataIndex: 'created_at',
key: 'created_at',
defaultSortOrder: 'descend',
sortOrder,
sorter: false,
span: 5,
render: (text: number) => (
<AutoTooltip ghost>
{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}
</AutoTooltip>
)
},
{
title: intl.formatMessage({ id: 'common.table.operation' }),
key: 'operation',
dataIndex: 'operation',
span: 4,
render: (text, record) => (
<DropdownButtons
items={setActionList(record)}
onSelect={(val) => handleSelect(val, record)}
/>
)
}
],
[sortOrder, intl]
);
return ( return (
<> <>
<PageContainer <PageContainer
@@ -776,6 +857,7 @@ const Models: React.FC<ModelsProps> = ({
} }
></PageTools> ></PageTools>
<SealTable <SealTable
columns={columns}
dataSource={dataSource} dataSource={dataSource}
rowSelection={rowSelection} rowSelection={rowSelection}
expandedRowKeys={expandedRowKeys} expandedRowKeys={expandedRowKeys}
@@ -800,100 +882,7 @@ const Models: React.FC<ModelsProps> = ({
hideOnSinglePage: queryParams.perPage === 10, hideOnSinglePage: queryParams.perPage === 10,
onChange: handlePageChange onChange: handlePageChange
}} }}
> ></SealTable>
<SealColumn
title={intl.formatMessage({ id: 'common.table.name' })}
dataIndex="name"
key="name"
width={400}
span={5}
render={(text, record: ListItem) => {
return (
<span
className="flex-center"
style={{
maxWidth: '100%'
}}
>
<AutoTooltip ghost>
<span className="m-r-5">{text}</span>
</AutoTooltip>
<ModelTag
categoryKey={record.categories?.[0] || ''}
></ModelTag>
</span>
);
}}
/>
<SealColumn
title={intl.formatMessage({ id: 'models.form.source' })}
dataIndex="source"
key="source"
span={6}
render={(text, record: ListItem) => {
return (
<span className="flex flex-column" style={{ width: '100%' }}>
<AutoTooltip ghost>{generateSource(record)}</AutoTooltip>
</span>
);
}}
/>
<SealColumn
title={
<Tooltip
title={intl.formatMessage({ id: 'models.form.replicas.tips' })}
>
{intl.formatMessage({ id: 'models.form.replicas' })}
<QuestionCircleOutlined className="m-l-5" />
</Tooltip>
}
dataIndex="replicas"
key="replicas"
align="center"
span={4}
editable={{
valueType: 'number',
title: intl.formatMessage({ id: 'models.table.replicas.edit' })
}}
render={(text, record: ListItem) => {
return (
<span style={{ paddingLeft: 10, minWidth: '33px' }}>
{record.ready_replicas} / {record.replicas}
</span>
);
}}
/>
<SealColumn
span={5}
title={intl.formatMessage({ id: 'common.table.createTime' })}
dataIndex="created_at"
key="created_at"
defaultSortOrder="descend"
sortOrder={sortOrder}
sorter={false}
render={(text, row) => {
return (
<AutoTooltip ghost>
{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}
</AutoTooltip>
);
}}
/>
<SealColumn
span={4}
title={intl.formatMessage({ id: 'common.table.operation' })}
key="operation"
dataIndex="operation"
render={(text, record) => {
return (
<DropdownButtons
items={setActionList(record)}
onSelect={(val) => handleSelect(val, record)}
></DropdownButtons>
);
}}
/>
</SealTable>
</PageContainer> </PageContainer>
<UpdateModel <UpdateModel
open={openAddModal} open={openAddModal}
+2
View File
@@ -60,6 +60,8 @@ export interface FormData {
} }
export interface ModelInstanceListItem { export interface ModelInstanceListItem {
backend?: string;
backend_version?: string;
source: string; source: string;
huggingface_repo_id: string; huggingface_repo_id: string;
huggingface_filename: string; huggingface_filename: string;
@@ -41,7 +41,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
token: '${mytoken}', token: '${mytoken}',
workerip: '${myworkerip}' workerip: '${myworkerip}'
}); });
}, [versionInfo, activeKey, props.token, origin]); }, [versionInfo, activeKey, props.token]);
return ( return (
<div className="container-install"> <div className="container-install">