feat: table list sorter

This commit is contained in:
jialin
2025-12-21 21:37:05 +08:00
parent 6738dfe0d3
commit 23a761226a
25 changed files with 417 additions and 138 deletions
@@ -1,15 +1,16 @@
import { Col, Row } from 'antd'; import { Col, Row } from 'antd';
import React from 'react'; import React from 'react';
import { SealColumnProps } from '../types'; import { OnSortFn, SealColumnProps } from '../types';
import TableHeader from './table-header'; import TableHeader from './table-header';
interface HeaderProps { interface HeaderProps {
columns: SealColumnProps[]; columns: SealColumnProps[];
onSort?: (dataIndex: string, order: any) => void; sortDirections?: ('ascend' | 'descend' | null)[];
onSort?: OnSortFn;
} }
const Header: React.FC<HeaderProps> = (props) => { const Header: React.FC<HeaderProps> = (props) => {
const { onSort } = props; const { onSort, sortDirections } = props;
return ( return (
<Row className="row"> <Row className="row">
@@ -32,6 +33,7 @@ const Header: React.FC<HeaderProps> = (props) => {
sorter={sorter} sorter={sorter}
dataIndex={dataIndex} dataIndex={dataIndex}
sortOrder={sortOrder} sortOrder={sortOrder}
sortDirections={sortDirections}
width={width} width={width}
defaultSortOrder={defaultSortOrder} defaultSortOrder={defaultSortOrder}
title={title} title={title}
@@ -14,19 +14,39 @@ const TableHeader: React.FC<TableHeaderProps> = (props) => {
firstCell, firstCell,
lastCell, lastCell,
sortOrder, sortOrder,
sortDirections = ['ascend', 'descend', null],
defaultSortOrder,
onSort, onSort,
sorter, sorter = false,
width, width,
dataIndex dataIndex
} = props; } = props;
const handleOnSort = () => { const [currentSortOrder, setCurrentSortOrder] = React.useState<
if (sortOrder === 'ascend') { 'ascend' | 'descend' | null
onSort?.(dataIndex, 'descend'); >(sortOrder || defaultSortOrder || null);
} else {
onSort?.(dataIndex, 'ascend'); const getNextSortOrder = (currentOrder: 'ascend' | 'descend' | null) => {
} const index = sortDirections.indexOf(currentOrder);
const nextIndex = (index + 1) % sortDirections.length;
return sortDirections[nextIndex];
}; };
const handleOnSort = () => {
setCurrentSortOrder((prev) => {
const next = getNextSortOrder(prev);
onSort?.(
{
columnKey: dataIndex,
field: dataIndex,
order: next
},
sorter
);
return next;
});
};
return ( return (
<div <div
style={{ width, ...style }} style={{ width, ...style }}
@@ -47,12 +67,12 @@ const TableHeader: React.FC<TableHeaderProps> = (props) => {
<span className="sorter"> <span className="sorter">
<CaretUpOutlined <CaretUpOutlined
className={classNames('sorter-up', { className={classNames('sorter-up', {
'sorter-active': sortOrder === 'ascend' 'sorter-active': currentSortOrder === 'ascend'
})} })}
></CaretUpOutlined> ></CaretUpOutlined>
<CaretDownOutlined <CaretDownOutlined
className={classNames('sorter-down', { className={classNames('sorter-down', {
'sorter-active': sortOrder === 'descend' 'sorter-active': currentSortOrder === 'descend'
})} })}
></CaretDownOutlined> ></CaretDownOutlined>
</span> </span>
+12 -2
View File
@@ -7,6 +7,7 @@ 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 { SealColumnProps, SealTableProps } from './types'; import { SealColumnProps, SealTableProps } from './types';
import useSorter from './use-sorter';
const Wrapper = styled.div<{ $token: any }>` const Wrapper = styled.div<{ $token: any }>`
--ant-table-cell-padding-inline: ${(props) => --ant-table-cell-padding-inline: ${(props) =>
@@ -32,7 +33,7 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
childParentKey, childParentKey,
onExpand, onExpand,
onExpandAll, onExpandAll,
onSort, onTableSort,
onCell, onCell,
expandedRowKeys, expandedRowKeys,
loading, loading,
@@ -43,10 +44,15 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
rowSelection, rowSelection,
pagination, pagination,
empty, empty,
sortDirections,
renderChildren, renderChildren,
loadChildren, loadChildren,
loadChildrenAPI loadChildrenAPI
} = props; } = props;
const { handleOnTableSort } = useSorter({
onTableSort,
columns
});
const { token } = theme.useToken(); const { token } = theme.useToken();
const parsedColumns = useMemo(() => { const parsedColumns = useMemo(() => {
if (columns) return columns; if (columns) return columns;
@@ -156,7 +162,11 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
disabled={!props.dataSource?.length} disabled={!props.dataSource?.length}
hasColumns={parsedColumns.length > 0} hasColumns={parsedColumns.length > 0}
></HeaderPrefix> ></HeaderPrefix>
<Header onSort={onSort} columns={parsedColumns}></Header> <Header
onSort={handleOnTableSort}
columns={parsedColumns}
sortDirections={sortDirections}
></Header>
</div> </div>
<Spin spinning={loading}> <Spin spinning={loading}>
<TableBody <TableBody
+27 -5
View File
@@ -1,5 +1,14 @@
import React from 'react'; import React from 'react';
export type OnSortFn = (
order: {
columnKey: string;
field: string;
order: 'ascend' | 'descend' | null;
},
sorter: boolean | { multiple?: number }
) => void;
export interface CellContentProps { export interface CellContentProps {
dataIndex: string; dataIndex: string;
render?: (text: any, record: any) => React.ReactNode; render?: (text: any, record: any) => React.ReactNode;
@@ -20,7 +29,7 @@ export interface SealColumnProps {
span: number; span: number;
align?: 'left' | 'center' | 'right'; align?: 'left' | 'center' | 'right';
headerStyle?: React.CSSProperties; headerStyle?: React.CSSProperties;
sorter?: boolean; sorter?: boolean | { multiple?: number };
defaultSortOrder?: 'ascend' | 'descend'; defaultSortOrder?: 'ascend' | 'descend';
editable?: editable?:
| boolean | boolean
@@ -34,17 +43,23 @@ export interface SealColumnProps {
} }
export interface TableHeaderProps { export interface TableHeaderProps {
sorter?: boolean; sorter?: boolean | { multiple?: number };
defaultSortOrder?: 'ascend' | 'descend'; sortDirections?: ('ascend' | 'descend' | null)[];
defaultSortOrder?: 'ascend' | 'descend' | null;
sortOrder?: 'ascend' | 'descend' | null; sortOrder?: 'ascend' | 'descend' | null;
dataIndex: string; dataIndex: string;
onSort?: (dataIndex: string, order: 'ascend' | 'descend') => void; onSort?: OnSortFn;
title: React.ReactNode; title: React.ReactNode;
style?: React.CSSProperties; style?: React.CSSProperties;
firstCell?: boolean; firstCell?: boolean;
lastCell?: boolean; lastCell?: boolean;
align?: 'left' | 'center' | 'right'; align?: 'left' | 'center' | 'right';
width?: number | string; width?: number | string;
sortedDataIndexList?: Array<{
columnKey: string;
field: string;
order: 'ascend' | 'descend' | null;
}>;
} }
export interface RowSelectionProps { export interface RowSelectionProps {
@@ -54,7 +69,14 @@ export interface RowSelectionProps {
removeSelectedKeys: (rowKeys: React.Key[]) => void; removeSelectedKeys: (rowKeys: React.Key[]) => void;
onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => void; onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => void;
} }
export type TableOrder = {
columnKey?: string;
field?: string;
order: 'ascend' | 'descend' | null;
};
export interface SealTableProps { export interface SealTableProps {
sortDirections?: ('ascend' | 'descend' | null)[];
columns?: SealColumnProps[]; columns?: SealColumnProps[];
childParentKey?: string; childParentKey?: string;
expandedRowKeys?: React.Key[]; expandedRowKeys?: React.Key[];
@@ -68,7 +90,7 @@ export interface SealTableProps {
loading?: boolean; loading?: boolean;
loadend?: boolean; loadend?: boolean;
onCell?: (record: any, dataIndex: string) => void; onCell?: (record: any, dataIndex: string) => void;
onSort?: (dataIndex: string, order: 'ascend' | 'descend') => void; onTableSort?: (order: TableOrder | Array<TableOrder>) => void;
onExpand?: (expanded: boolean, record: any, rowKey: any) => void; onExpand?: (expanded: boolean, record: any, rowKey: any) => void;
onExpandAll?: (expanded: boolean) => void; onExpandAll?: (expanded: boolean) => void;
renderChildren?: ( renderChildren?: (
+84
View File
@@ -0,0 +1,84 @@
import { isBoolean } from 'lodash';
import { useRef } from 'react';
import { SealColumnProps, TableOrder } from './types';
const initSorterList = (columns: SealColumnProps[]) => {
const list = columns.filter((col) => col.defaultSortOrder);
if (list.length === 0) {
return [];
}
return list.map((col) => ({
columnKey: col.key || col.dataIndex,
field: col.dataIndex,
order: col.defaultSortOrder || null
}));
};
export default function useSorter(options: {
onTableSort?: (TableOrder: TableOrder | Array<TableOrder>) => void;
columns?: SealColumnProps[];
}) {
const { onTableSort, columns } = options;
const sorterListRef = useRef<TableOrder | Array<TableOrder>>(
initSorterList(columns || [])
);
const handleOnTableSort = (
order: TableOrder,
sorter: boolean | { multiple?: number }
) => {
let currentOrder: TableOrder = { ...order };
if (order.order === null) {
currentOrder = {
columnKey: undefined,
field: undefined,
order: null
};
}
// single column sort
if (isBoolean(sorter)) {
sorterListRef.current = currentOrder;
onTableSort?.(sorterListRef.current);
return;
}
// multi column sort
if (sorter && typeof sorter === 'object' && sorter.multiple) {
if (!Array.isArray(sorterListRef.current)) {
if (
sorterListRef.current.columnKey === currentOrder.columnKey ||
sorterListRef.current.field === currentOrder.field
) {
// remove the sorter if order is null
sorterListRef.current = {
...currentOrder
};
} else {
sorterListRef.current = [sorterListRef.current, { ...currentOrder }];
}
} else if (Array.isArray(sorterListRef.current)) {
const existingIndex = sorterListRef.current.findIndex(
(item) =>
item.columnKey === order.columnKey || item.field === order.field
);
if (existingIndex !== -1) {
sorterListRef.current.splice(existingIndex, 1);
sorterListRef.current.push({ ...currentOrder });
} else {
sorterListRef.current.push({ ...currentOrder });
}
}
}
onTableSort?.(sorterListRef.current);
};
return {
sorterListRef,
handleOnTableSort
};
}
+7
View File
@@ -9,3 +9,10 @@ export const DEFAULT_ENTER_PAGE = {
export const GPUSTACK_API_BASE_URL = 'v2'; export const GPUSTACK_API_BASE_URL = 'v2';
export const OPENAI_COMPATIBLE = 'v1'; export const OPENAI_COMPATIBLE = 'v1';
type SortDirection = 'ascend' | 'descend' | null;
export const TABLE_SORT_DIRECTIONS: SortDirection[] = [
'ascend',
'descend',
null
];
+18 -11
View File
@@ -1,3 +1,4 @@
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import useSetChunkRequest from '@/hooks/use-chunk-request'; import useSetChunkRequest from '@/hooks/use-chunk-request';
import useTableRowSelection from '@/hooks/use-table-row-selection'; import useTableRowSelection from '@/hooks/use-table-row-selection';
import useUpdateChunkedList from '@/hooks/use-update-chunk-list'; import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
@@ -5,6 +6,7 @@ import { handleBatchRequest } from '@/utils';
import _ from 'lodash'; import _ from 'lodash';
import qs from 'query-string'; import qs from 'query-string';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useTableMultiSort } from './use-table-sort';
type EventsType = 'CREATE' | 'UPDATE' | 'DELETE' | 'INSERT'; type EventsType = 'CREATE' | 'UPDATE' | 'DELETE' | 'INSERT';
@@ -51,13 +53,7 @@ export default function useTableFetch<T>(
const chunkRequedtRef = useRef<any>(null); const chunkRequedtRef = useRef<any>(null);
const modalRef = useRef<any>(null); const modalRef = useRef<any>(null);
const rowSelection = useTableRowSelection(); const rowSelection = useTableRowSelection();
const [sortOrder, setSortOrder] = useState<{ const { sortOrder, handleMultiSortChange } = useTableMultiSort();
order?: 'ascend' | 'descend' | null;
columnKey?: string;
}>({
order: null,
columnKey: undefined
});
// for skeleton loading // for skeleton loading
const [extraStatus, setExtraStatus] = useState<Record<string, any>>({ const [extraStatus, setExtraStatus] = useState<Record<string, any>>({
@@ -81,6 +77,7 @@ export default function useTableFetch<T>(
page: 1, page: 1,
perPage: 10, perPage: 10,
search: '', search: '',
sort_by: '',
...defaultQueryParams ...defaultQueryParams
}); });
@@ -106,7 +103,6 @@ export default function useTableFetch<T>(
const updateHandler = (list: any) => { const updateHandler = (list: any) => {
_.each(list, (data: any) => { _.each(list, (data: any) => {
console.log('list================:', list);
updateChunkedList(data); updateChunkedList(data);
}); });
}; };
@@ -238,9 +234,19 @@ export default function useTableFetch<T>(
}; };
const handleTableChange = (pagination: any, filters: any, sorter: any) => { const handleTableChange = (pagination: any, filters: any, sorter: any) => {
setSortOrder({ const sortKeys = handleMultiSortChange(sorter);
order: sorter.order, setQueryParams((pre: any) => {
columnKey: sorter.columnKey || sorter.field return {
...pre,
sort_by: sortKeys.join(',')
};
});
fetchData({
query: {
...queryParams,
page: 1,
sort_by: sortKeys.join(',')
}
}); });
}; };
@@ -337,6 +343,7 @@ export default function useTableFetch<T>(
queryParams, queryParams,
modalRef, modalRef,
extraStatus, extraStatus,
TABLE_SORT_DIRECTIONS,
setQueryParams, setQueryParams,
handleDelete, handleDelete,
handleDeleteBatch, handleDeleteBatch,
+29
View File
@@ -23,3 +23,32 @@ export default function useTableSort({
setSortOrder: handleSortChange setSortOrder: handleSortChange
}; };
} }
type orderType = {
columnKey?: string;
field?: string;
order: SortOrder;
};
export function useTableMultiSort() {
const [sortOrder, setSortOrder] = useState<string[]>([]);
const handleMultiSortChange = (order: orderType | orderType[]) => {
const sortOrders = Array.isArray(order) ? order : [order];
const sortOrderMap: string[] = [];
sortOrders.forEach((item) => {
const key = item.columnKey || item.field;
if (key) {
sortOrderMap.push(item.order === 'descend' ? `-${key}` : key);
}
});
setSortOrder(sortOrderMap);
return sortOrderMap;
};
return {
sortOrder,
handleMultiSortChange
};
}
+11 -10
View File
@@ -10,10 +10,7 @@ import { ListItem } from '../config/types';
interface ColumnsHookProps { interface ColumnsHookProps {
handleSelect: (val: string, record: ListItem) => void; handleSelect: (val: string, record: ListItem) => void;
sortOrder: { sortOrder: string;
order?: 'ascend' | 'descend' | null;
columnKey?: string;
};
} }
const actionList: Global.ActionItem[] = [ const actionList: Global.ActionItem[] = [
@@ -42,6 +39,9 @@ const useModelsColumns = ({
title: intl.formatMessage({ id: 'common.table.name' }), title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
sorter: {
multiple: 1
},
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<AutoTooltip ghost style={{ maxWidth: 400 }}> <AutoTooltip ghost style={{ maxWidth: 400 }}>
{text} {text}
@@ -52,6 +52,9 @@ const useModelsColumns = ({
title: intl.formatMessage({ id: 'apikeys.form.expiretime' }), title: intl.formatMessage({ id: 'apikeys.form.expiretime' }),
dataIndex: 'expires_at', dataIndex: 'expires_at',
key: 'expires_at', key: 'expires_at',
sorter: {
multiple: 2
},
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<AutoTooltip ghost> <AutoTooltip ghost>
{text {text
@@ -93,11 +96,9 @@ const useModelsColumns = ({
dataIndex: 'created_at', dataIndex: 'created_at',
key: 'created_at', key: 'created_at',
defaultSortOrder: 'descend', defaultSortOrder: 'descend',
sortOrder: sorter: {
sortOrder.order && sortOrder.columnKey === 'created_at' multiple: 3
? sortOrder.order },
: null,
sorter: false,
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
@@ -120,7 +121,7 @@ const useModelsColumns = ({
) )
} }
]; ];
}, [sortOrder, intl, handleSelect]); }, [intl, handleSelect]);
}; };
export default useModelsColumns; export default useModelsColumns;
+6 -2
View File
@@ -17,6 +17,7 @@ import useKeysColumns from './hooks/use-keys-columns';
const APIKeys: React.FC = () => { const APIKeys: React.FC = () => {
const { const {
TABLE_SORT_DIRECTIONS,
dataSource, dataSource,
rowSelection, rowSelection,
queryParams, queryParams,
@@ -32,7 +33,10 @@ const APIKeys: React.FC = () => {
} = useTableFetch<ListItem>({ } = useTableFetch<ListItem>({
fetchAPI: queryApisKeysList, fetchAPI: queryApisKeysList,
deleteAPI: deleteApisKey, deleteAPI: deleteApisKey,
contentForDelete: 'apikeys.table.apikeys' contentForDelete: 'apikeys.table.apikeys',
defaultQueryParams: {
sort_by: '-created_at'
}
}); });
const intl = useIntl(); const intl = useIntl();
@@ -139,7 +143,7 @@ const APIKeys: React.FC = () => {
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowSelection={rowSelection} rowSelection={rowSelection}
loading={dataSource.loading} loading={dataSource.loading}
sortDirections={['ascend', 'descend', 'ascend']} sortDirections={TABLE_SORT_DIRECTIONS}
rowKey="id" rowKey="id"
onChange={handleTableChange} onChange={handleTableChange}
pagination={{ pagination={{
+13 -1
View File
@@ -4,7 +4,9 @@ import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools'; import { FilterBar } from '@/components/page-tools';
import SealTable from '@/components/seal-table'; import SealTable from '@/components/seal-table';
import TableContext from '@/components/seal-table/table-context'; import TableContext from '@/components/seal-table/table-context';
import { TableOrder } from '@/components/seal-table/types';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import type { PageActionType } from '@/config/types'; import type { PageActionType } from '@/config/types';
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys'; import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
import useTableFetch from '@/hooks/use-table-fetch'; import useTableFetch from '@/hooks/use-table-fetch';
@@ -54,6 +56,7 @@ const Clusters: React.FC = () => {
rowSelection, rowSelection,
queryParams, queryParams,
modalRef, modalRef,
handleTableChange,
handleDelete, handleDelete,
handleDeleteBatch, handleDeleteBatch,
fetchData, fetchData,
@@ -65,7 +68,10 @@ const Clusters: React.FC = () => {
deleteAPI: deleteCluster, deleteAPI: deleteCluster,
watch: true, watch: true,
API: CLUSTERS_API, API: CLUSTERS_API,
contentForDelete: 'menu.clusterManagement.clusters' contentForDelete: 'menu.clusterManagement.clusters',
defaultQueryParams: {
sort_by: '-created_at'
}
}); });
const { watchDataList: allWorkerPoolList } = useWatchList(WORKER_POOLS_API); const { watchDataList: allWorkerPoolList } = useWatchList(WORKER_POOLS_API);
const [expandAtom] = useAtom(expandKeysAtom); const [expandAtom] = useAtom(expandKeysAtom);
@@ -227,6 +233,10 @@ const Clusters: React.FC = () => {
} }
); );
const handleOnSortChange = (order: TableOrder | Array<TableOrder>) => {
handleTableChange({}, {}, order);
};
const setDisableExpand = (row: ClusterListItem) => { const setDisableExpand = (row: ClusterListItem) => {
return ( return (
row.provider !== ProviderValueMap.DigitalOcean || row.provider !== ProviderValueMap.DigitalOcean ||
@@ -314,10 +324,12 @@ const Clusters: React.FC = () => {
<SealTable <SealTable
rowKey="id" rowKey="id"
loadChildren={getWorkerPoolList} loadChildren={getWorkerPoolList}
sortDirections={TABLE_SORT_DIRECTIONS}
expandedRowKeys={expandedRowKeys} expandedRowKeys={expandedRowKeys}
onExpand={handleExpandChange} onExpand={handleExpandChange}
onExpandAll={handleToggleExpandAll} onExpandAll={handleToggleExpandAll}
renderChildren={renderChildren} renderChildren={renderChildren}
onTableSort={handleOnSortChange}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
loading={dataSource.loading} loading={dataSource.loading}
loadend={dataSource.loadend} loadend={dataSource.loadend}
+4 -1
View File
@@ -58,7 +58,10 @@ const Credentials: React.FC = () => {
} = useTableFetch<ListItem>({ } = useTableFetch<ListItem>({
fetchAPI: queryCredentialList, fetchAPI: queryCredentialList,
deleteAPI: deleteCredential, deleteAPI: deleteCredential,
contentForDelete: 'menu.clusterManagement.credentials' contentForDelete: 'menu.clusterManagement.credentials',
defaultQueryParams: {
sort_by: '-created_at'
}
}); });
const [isFromCluster, setIsFromCluster] = useAtom(fromClusterCreationAtom); const [isFromCluster, setIsFromCluster] = useAtom(fromClusterCreationAtom);
const intl = useIntl(); const intl = useIntl();
@@ -33,6 +33,9 @@ const useClusterColumns = (
{ {
title: intl.formatMessage({ id: 'common.table.name' }), title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'name', dataIndex: 'name',
sorter: {
multiple: 1
},
span: 3, span: 3,
render: (text: string, record: ClusterListItem) => ( render: (text: string, record: ClusterListItem) => (
<AutoTooltip ghost>{text}</AutoTooltip> <AutoTooltip ghost>{text}</AutoTooltip>
@@ -41,6 +44,9 @@ const useClusterColumns = (
{ {
title: intl.formatMessage({ id: 'clusters.table.provider' }), title: intl.formatMessage({ id: 'clusters.table.provider' }),
dataIndex: 'provider', dataIndex: 'provider',
sorter: {
multiple: 2
},
span: 4, span: 4,
render: (value: string) => ( render: (value: string) => (
<AutoTooltip ghost minWidth={20}> <AutoTooltip ghost minWidth={20}>
@@ -86,6 +92,10 @@ const useClusterColumns = (
{ {
title: intl.formatMessage({ id: 'common.table.createTime' }), title: intl.formatMessage({ id: 'common.table.createTime' }),
dataIndex: 'created_at', dataIndex: 'created_at',
defaultSortOrder: 'descend',
sorter: {
multiple: 5
},
span: 4, span: 4,
render: (value: string) => ( render: (value: string) => (
<AutoTooltip ghost minWidth={20}> <AutoTooltip ghost minWidth={20}>
@@ -23,6 +23,9 @@ const useCredentialColumns = (
{ {
title: intl.formatMessage({ id: 'common.table.name' }), title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'name', dataIndex: 'name',
sorter: {
multiple: 1
},
render: (text: string) => ( render: (text: string) => (
<AutoTooltip ghost minWidth={20}> <AutoTooltip ghost minWidth={20}>
{text} {text}
@@ -32,6 +35,7 @@ const useCredentialColumns = (
{ {
title: intl.formatMessage({ id: 'clusters.table.provider' }), title: intl.formatMessage({ id: 'clusters.table.provider' }),
dataIndex: 'provider', dataIndex: 'provider',
sorter: false,
render: (value: string) => <span>{ProviderLabelMap[value]}</span> render: (value: string) => <span>{ProviderLabelMap[value]}</span>
}, },
{ {
@@ -39,11 +43,9 @@ const useCredentialColumns = (
dataIndex: 'created_at', dataIndex: 'created_at',
showSorterTooltip: false, showSorterTooltip: false,
defaultSortOrder: 'descend', defaultSortOrder: 'descend',
sortOrder: sorter: {
sortOrder.order && sortOrder.columnKey === 'created_at' multiple: 3
? sortOrder.order },
: null,
sorter: false,
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
+10 -13
View File
@@ -6,7 +6,9 @@ import { PageSize } from '@/components/logs-viewer/config';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import BaseSelect from '@/components/seal-form/base/select'; import BaseSelect from '@/components/seal-form/base/select';
import SealTable from '@/components/seal-table'; import SealTable from '@/components/seal-table';
import { TableOrder } from '@/components/seal-table/types';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import { PageActionType } from '@/config/types'; import { PageActionType } from '@/config/types';
import useBodyScroll from '@/hooks/use-body-scroll'; import useBodyScroll from '@/hooks/use-body-scroll';
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys'; import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
@@ -75,6 +77,8 @@ interface ModelsProps {
handleOnToggleExpandAll: () => void; handleOnToggleExpandAll: () => void;
onStop?: (ids: number[]) => void; onStop?: (ids: number[]) => void;
onStart?: () => void; onStart?: () => void;
onTableSort?: (order: TableOrder | Array<TableOrder>) => void;
sortOrder: string[];
queryParams: { queryParams: {
page: number; page: number;
perPage: number; perPage: number;
@@ -114,6 +118,8 @@ const Models: React.FC<ModelsProps> = ({
handleClusterChange, handleClusterChange,
onStop, onStop,
onStart, onStart,
onTableSort,
sortOrder,
deleteIds, deleteIds,
dataSource, dataSource,
queryParams, queryParams,
@@ -148,13 +154,6 @@ const Models: React.FC<ModelsProps> = ({
removeExpandedRowKey, removeExpandedRowKey,
expandedRowKeys expandedRowKeys
} = useExpandedRowKeys(expandAtom); } = useExpandedRowKeys(expandAtom);
const [sortOrder, setSortOrder] = useState<{
columnKey: string;
order: 'ascend' | 'descend' | null;
}>({
order: null,
columnKey: ''
});
const [apiAccessInfo, setAPIAccessInfo] = useState<any>({ const [apiAccessInfo, setAPIAccessInfo] = useState<any>({
show: false, show: false,
@@ -219,11 +218,8 @@ const Models: React.FC<ModelsProps> = ({
currentData.current = data; currentData.current = data;
}; };
const handleOnSort = (dataIndex: string, order: any) => { const handleOnSort = (order: TableOrder | Array<TableOrder>) => {
setSortOrder({ onTableSort?.(order);
columnKey: dataIndex,
order: order
});
}; };
const handleOnCell = useMemoizedFn(async (record: any, extra: any) => { const handleOnCell = useMemoizedFn(async (record: any, extra: any) => {
@@ -710,6 +706,7 @@ const Models: React.FC<ModelsProps> = ({
<SealTable <SealTable
columns={columns} columns={columns}
sortDirections={TABLE_SORT_DIRECTIONS}
dataSource={dataSource} dataSource={dataSource}
rowSelection={rowSelection} rowSelection={rowSelection}
expandedRowKeys={expandedRowKeys} expandedRowKeys={expandedRowKeys}
@@ -720,7 +717,7 @@ const Models: React.FC<ModelsProps> = ({
rowKey="id" rowKey="id"
childParentKey="model_id" childParentKey="model_id"
expandable={true} expandable={true}
onSort={handleOnSort} onTableSort={handleOnSort}
onCell={handleOnCell} onCell={handleOnCell}
pollingChildren={false} pollingChildren={false}
watchChildren={true} watchChildren={true}
+20 -19
View File
@@ -6,7 +6,6 @@ import { OPENAI_COMPATIBLE } from '@/config/settings';
import { QuestionCircleOutlined } from '@ant-design/icons'; import { QuestionCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Tooltip } from 'antd'; import { Tooltip } from 'antd';
import type { SortOrder } from 'antd/es/table/interface';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import { useMemo } from 'react'; import { useMemo } from 'react';
@@ -34,10 +33,7 @@ const setModelActionList = (record: any) => {
interface ModelsColumnsHookProps { interface ModelsColumnsHookProps {
handleSelect: (val: string, record: ListItem) => void; handleSelect: (val: string, record: ListItem) => void;
sortOrder: { sortOrder: string[];
columnKey: string;
order: SortOrder;
};
clusterList: Global.BaseOption< clusterList: Global.BaseOption<
number, number,
{ provider: string; state: string | number } { provider: string; state: string | number }
@@ -57,11 +53,9 @@ const useModelsColumns = ({
title: intl.formatMessage({ id: 'common.table.name' }), title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
sortOrder: sorter: {
sortOrder.order && sortOrder.columnKey === 'name' multiple: 1
? sortOrder.order },
: null,
sorter: false,
span: 5, span: 5,
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<span className="flex-center" style={{ maxWidth: '100%' }}> <span className="flex-center" style={{ maxWidth: '100%' }}>
@@ -74,8 +68,11 @@ const useModelsColumns = ({
}, },
{ {
title: intl.formatMessage({ id: 'clusters.title' }), title: intl.formatMessage({ id: 'clusters.title' }),
dataIndex: 'cluster', dataIndex: 'cluster_id',
key: 'cluster', key: 'cluster_id',
sorter: {
multiple: 2
},
span: 3, span: 3,
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<span className="flex flex-column" style={{ width: '100%' }}> <span className="flex flex-column" style={{ width: '100%' }}>
@@ -90,6 +87,9 @@ const useModelsColumns = ({
title: intl.formatMessage({ id: 'models.form.source' }), title: intl.formatMessage({ id: 'models.form.source' }),
dataIndex: 'source', dataIndex: 'source',
key: 'source', key: 'source',
sorter: {
multiple: 3
},
span: 5, span: 5,
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<span className="flex flex-column" style={{ width: '100%' }}> <span className="flex flex-column" style={{ width: '100%' }}>
@@ -111,9 +111,12 @@ const useModelsColumns = ({
<QuestionCircleOutlined className="m-l-5" /> <QuestionCircleOutlined className="m-l-5" />
</Tooltip> </Tooltip>
), ),
dataIndex: 'replicas', dataIndex: 'ready_replicas',
key: 'replicas', key: 'ready_replicas',
align: 'center', align: 'center',
sorter: {
multiple: 4
},
span: 4, span: 4,
editable: { editable: {
valueType: 'number', valueType: 'number',
@@ -130,11 +133,9 @@ const useModelsColumns = ({
dataIndex: 'created_at', dataIndex: 'created_at',
key: 'created_at', key: 'created_at',
defaultSortOrder: 'descend', defaultSortOrder: 'descend',
sortOrder: sorter: {
sortOrder.order && sortOrder.columnKey === 'created_at' multiple: 5
? sortOrder.order },
: null,
sorter: false,
span: 4, span: 4,
render: (text: number) => ( render: (text: number) => (
<AutoTooltip ghost> <AutoTooltip ghost>
+25 -1
View File
@@ -1,5 +1,7 @@
import TableContext from '@/components/seal-table/table-context'; import TableContext from '@/components/seal-table/table-context';
import { TableOrder } from '@/components/seal-table/types';
import useSetChunkRequest from '@/hooks/use-chunk-request'; import useSetChunkRequest from '@/hooks/use-chunk-request';
import { useTableMultiSort } from '@/hooks/use-table-sort';
import useUpdateChunkedList from '@/hooks/use-update-chunk-list'; import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import _ from 'lodash'; import _ from 'lodash';
@@ -15,6 +17,7 @@ import TableList from './components/table-list';
import { ListItem } from './config/types'; import { ListItem } from './config/types';
const Models: React.FC = () => { const Models: React.FC = () => {
const { sortOrder, handleMultiSortChange } = useTableMultiSort();
const { setChunkRequest, createAxiosToken } = useSetChunkRequest(); const { setChunkRequest, createAxiosToken } = useSetChunkRequest();
const { setChunkRequest: setModelInstanceChunkRequest } = const { setChunkRequest: setModelInstanceChunkRequest } =
useSetChunkRequest(); useSetChunkRequest();
@@ -43,7 +46,8 @@ const Models: React.FC = () => {
perPage: 10, perPage: 10,
search: '', search: '',
cluster_id: 0, cluster_id: 0,
categories: [] categories: [],
sort_by: '-created_at'
}); });
const { updateChunkedList, cacheDataListRef } = useUpdateChunkedList({ const { updateChunkedList, cacheDataListRef } = useUpdateChunkedList({
@@ -96,6 +100,7 @@ const Models: React.FC = () => {
perPage: number; perPage: number;
search: string; search: string;
categories: any[]; categories: any[];
sort_by: string;
}; };
}) => { }) => {
const { loadingVal, query } = params || {}; const { loadingVal, query } = params || {};
@@ -294,6 +299,23 @@ const Models: React.FC = () => {
}); });
}; };
const handleOnSortChange = (order: TableOrder | Array<TableOrder>) => {
const sortKeys = handleMultiSortChange(order);
setQueryParams((pre: any) => {
return {
...pre,
sort_by: sortKeys.join(',')
};
});
fetchData({
query: {
...queryParams,
page: 1,
sort_by: sortKeys.join(',')
}
});
};
useEffect(() => { useEffect(() => {
let timer: any = null; let timer: any = null;
// fetch data first time // fetch data first time
@@ -397,6 +419,8 @@ const Models: React.FC = () => {
onCancelViewLogs={handleOnCancelViewLogs} onCancelViewLogs={handleOnCancelViewLogs}
onStop={handleSearchBySilent} onStop={handleSearchBySilent}
onStart={handleSearchBySilent} onStart={handleSearchBySilent}
onTableSort={handleOnSortChange}
sortOrder={sortOrder}
queryParams={queryParams} queryParams={queryParams}
loading={dataSource.loading} loading={dataSource.loading}
loadend={dataSource.loadend} loadend={dataSource.loadend}
+2 -1
View File
@@ -1,5 +1,6 @@
import IconFont from '@/components/icon-font'; import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools'; import { FilterBar } from '@/components/page-tools';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import useTableFetch from '@/hooks/use-table-fetch'; import useTableFetch from '@/hooks/use-table-fetch';
import NoResult from '@/pages/_components/no-result'; import NoResult from '@/pages/_components/no-result';
import PageBox from '@/pages/_components/page-box'; import PageBox from '@/pages/_components/page-box';
@@ -102,7 +103,7 @@ const GPUList: React.FC = () => {
<Table <Table
columns={columns} columns={columns}
style={{ width: '100%' }} style={{ width: '100%' }}
sortDirections={['ascend', 'descend', 'ascend']} sortDirections={TABLE_SORT_DIRECTIONS}
tableLayout={dataSource.loadend ? 'auto' : 'fixed'} tableLayout={dataSource.loadend ? 'auto' : 'fixed'}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
loading={dataSource.loading} loading={dataSource.loading}
@@ -3,6 +3,7 @@ import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font'; import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools'; import { FilterBar } from '@/components/page-tools';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import useAppUtils from '@/hooks/use-app-utils'; import useAppUtils from '@/hooks/use-app-utils';
import useBodyScroll from '@/hooks/use-body-scroll'; import useBodyScroll from '@/hooks/use-body-scroll';
import useTableFetch from '@/hooks/use-table-fetch'; import useTableFetch from '@/hooks/use-table-fetch';
@@ -67,7 +68,10 @@ const ModelFiles = () => {
deleteAPI: deleteModelFile, deleteAPI: deleteModelFile,
API: MODEL_FILES_API, API: MODEL_FILES_API,
watch: true, watch: true,
contentForDelete: 'resources.modelfiles.modelfile' contentForDelete: 'resources.modelfiles.modelfile',
defaultQueryParams: {
sort_by: '-created_at'
}
}); });
const intl = useIntl(); const intl = useIntl();
const { showSuccess } = useAppUtils(); const { showSuccess } = useAppUtils();
@@ -316,6 +320,7 @@ const ModelFiles = () => {
<Table <Table
rowKey="id" rowKey="id"
tableLayout="fixed" tableLayout="fixed"
sortDirections={TABLE_SORT_DIRECTIONS}
style={{ width: '100%' }} style={{ width: '100%' }}
onChange={handleTableChange} onChange={handleTableChange}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
+6 -2
View File
@@ -1,5 +1,6 @@
import DeleteModal from '@/components/delete-modal'; import DeleteModal from '@/components/delete-modal';
import { FilterBar } from '@/components/page-tools'; import { FilterBar } from '@/components/page-tools';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import useTableFetch from '@/hooks/use-table-fetch'; import useTableFetch from '@/hooks/use-table-fetch';
import PageBox from '@/pages/_components/page-box'; import PageBox from '@/pages/_components/page-box';
import { queryClusterList } from '@/pages/cluster-management/apis'; import { queryClusterList } from '@/pages/cluster-management/apis';
@@ -51,7 +52,10 @@ const Workers: React.FC = () => {
events: ['UPDATE', 'DELETE', 'INSERT'], events: ['UPDATE', 'DELETE', 'INSERT'],
contentForDelete: 'resources.worker', contentForDelete: 'resources.worker',
watch: true, watch: true,
API: WORKERS_API API: WORKERS_API,
defaultQueryParams: {
sort_by: '-created_at'
}
}); });
const { TerminalPanel, terminals, handleAddTerminal } = useTerminalTabs(); const { TerminalPanel, terminals, handleAddTerminal } = useTerminalTabs();
const { MaintenanceModal, handleStopMaintenance, setOpenStatus } = const { MaintenanceModal, handleStopMaintenance, setOpenStatus } =
@@ -270,7 +274,7 @@ const Workers: React.FC = () => {
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
columns={columns} columns={columns}
sortDirections={['ascend', 'descend', 'ascend']} sortDirections={TABLE_SORT_DIRECTIONS}
tableLayout={dataSource.loadend ? 'auto' : 'fixed'} tableLayout={dataSource.loadend ? 'auto' : 'fixed'}
style={{ width: '100%' }} style={{ width: '100%' }}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
+17 -11
View File
@@ -255,10 +255,7 @@ const getWorkerName = (
const useFilesColumns = (props: { const useFilesColumns = (props: {
handleSelect: (action: string, record: ListItem) => void; handleSelect: (action: string, record: ListItem) => void;
workersList: Global.BaseOption<number>[]; workersList: Global.BaseOption<number>[];
sortOrder: { sortOrder: string[];
order?: 'ascend' | 'descend' | null;
columnKey?: string;
};
}): ColumnsType<ListItem> => { }): ColumnsType<ListItem> => {
const { workersList, sortOrder, handleSelect } = props; const { workersList, sortOrder, handleSelect } = props;
const intl = useIntl(); const intl = useIntl();
@@ -268,6 +265,9 @@ const useFilesColumns = (props: {
{ {
title: intl.formatMessage({ id: 'models.form.source' }), title: intl.formatMessage({ id: 'models.form.source' }),
dataIndex: 'source', dataIndex: 'source',
sorter: {
multiple: 1
},
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
@@ -285,7 +285,10 @@ const useFilesColumns = (props: {
}, },
{ {
title: intl.formatMessage({ id: 'resources.worker' }), title: intl.formatMessage({ id: 'resources.worker' }),
dataIndex: 'worker_name', dataIndex: 'worker_id',
sorter: {
multiple: 2
},
width: '18%', width: '18%',
ellipsis: { ellipsis: {
showTitle: false showTitle: false
@@ -309,6 +312,9 @@ const useFilesColumns = (props: {
{ {
title: intl.formatMessage({ id: 'resources.modelfiles.form.path' }), title: intl.formatMessage({ id: 'resources.modelfiles.form.path' }),
dataIndex: 'resolved_paths', dataIndex: 'resolved_paths',
sorter: {
multiple: 3
},
width: '20%', width: '20%',
ellipsis: { ellipsis: {
showTitle: false showTitle: false
@@ -336,15 +342,15 @@ const useFilesColumns = (props: {
{ {
title: intl.formatMessage({ id: 'common.table.createTime' }), title: intl.formatMessage({ id: 'common.table.createTime' }),
dataIndex: 'created_at', dataIndex: 'created_at',
sorter: false, defaultSortOrder: 'descend',
key: 'created_at',
sorter: {
multiple: 4
},
width: 180, width: 180,
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
sortOrder:
sortOrder.order && sortOrder.columnKey === 'created_at'
? sortOrder.order
: null,
render: (text: number) => ( render: (text: number) => (
<AutoTooltip ghost minWidth={20}> <AutoTooltip ghost minWidth={20}>
{dayjs(text).format('YYYY-MM-DD HH:mm:ss')} {dayjs(text).format('YYYY-MM-DD HH:mm:ss')}
@@ -363,7 +369,7 @@ const useFilesColumns = (props: {
) )
} }
]; ];
}, [intl, sortOrder, workersList, handleSelect]); }, [intl, workersList, handleSelect]);
}; };
export default useFilesColumns; export default useFilesColumns;
+28 -15
View File
@@ -39,10 +39,7 @@ const useGPUColumns = (props: {
loadend: boolean; loadend: boolean;
firstLoad: boolean; firstLoad: boolean;
clusterList: Global.BaseOption<number>[]; clusterList: Global.BaseOption<number>[];
sortOrder: { sortOrder: string[];
order?: 'ascend' | 'descend' | null;
columnKey?: string;
};
}): ColumnsType<GPUDeviceItem> => { }): ColumnsType<GPUDeviceItem> => {
const { clusterList, loadend, firstLoad, sortOrder } = props; const { clusterList, loadend, firstLoad, sortOrder } = props;
const intl = useIntl(); const intl = useIntl();
@@ -53,11 +50,9 @@ const useGPUColumns = (props: {
title: intl.formatMessage({ id: 'common.table.name' }), title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'name', dataIndex: 'name',
width: 240, width: 240,
sorter: false, sorter: {
sortOrder: multiple: 1
sortOrder.order && sortOrder.columnKey === 'name' },
? sortOrder.order
: null,
render: (text: string, record: GPUDeviceItem) => ( render: (text: string, record: GPUDeviceItem) => (
<AutoTooltip ghost maxWidth={240}> <AutoTooltip ghost maxWidth={240}>
{text} {text}
@@ -67,11 +62,17 @@ const useGPUColumns = (props: {
{ {
title: intl.formatMessage({ id: 'resources.table.index' }), title: intl.formatMessage({ id: 'resources.table.index' }),
dataIndex: 'index', dataIndex: 'index',
sorter: {
multiple: 2
},
render: (text: string, record: GPUDeviceItem) => <span>{text}</span> render: (text: string, record: GPUDeviceItem) => <span>{text}</span>
}, },
{ {
title: intl.formatMessage({ id: 'clusters.title' }), title: intl.formatMessage({ id: 'clusters.title' }),
dataIndex: 'cluster_id', dataIndex: 'cluster_id',
sorter: {
multiple: 3
},
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
@@ -84,6 +85,9 @@ const useGPUColumns = (props: {
{ {
title: intl.formatMessage({ id: 'resources.worker' }), title: intl.formatMessage({ id: 'resources.worker' }),
dataIndex: 'worker_name', dataIndex: 'worker_name',
sorter: {
multiple: 4
},
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
@@ -93,7 +97,10 @@ const useGPUColumns = (props: {
}, },
{ {
title: intl.formatMessage({ id: 'resources.table.vender' }), title: intl.formatMessage({ id: 'resources.table.vender' }),
dataIndex: 'vendor' dataIndex: 'vendor',
sorter: {
multiple: 5
}
}, },
{ {
title: `${intl.formatMessage({ id: 'resources.table.temperature' })} (°C)`, title: `${intl.formatMessage({ id: 'resources.table.temperature' })} (°C)`,
@@ -104,8 +111,11 @@ const useGPUColumns = (props: {
}, },
{ {
title: `${intl.formatMessage({ id: 'resources.table.utilization' })}`, title: `${intl.formatMessage({ id: 'resources.table.utilization' })}`,
dataIndex: 'gpuUtil', dataIndex: 'core.utilization_rate',
key: 'gpuUtil', key: 'core.utilization_rate',
sorter: {
multiple: 6
},
render: (text: number, record: GPUDeviceItem) => { render: (text: number, record: GPUDeviceItem) => {
return ( return (
<> <>
@@ -122,8 +132,11 @@ const useGPUColumns = (props: {
}, },
{ {
title: intl.formatMessage({ id: 'resources.table.vramutilization' }), title: intl.formatMessage({ id: 'resources.table.vramutilization' }),
dataIndex: 'VRAM', dataIndex: 'memory.utilization_rate',
key: 'VRAM', key: 'memory.utilization_rate',
sorter: {
multiple: 7
},
render: (text: number, record: GPUDeviceItem, index: number) => { render: (text: number, record: GPUDeviceItem, index: number) => {
return ( return (
<ProgressBar <ProgressBar
@@ -147,7 +160,7 @@ const useGPUColumns = (props: {
} }
} }
]; ];
}, [intl, sortOrder, clusterList, loadend, firstLoad]); }, [intl, clusterList, loadend, firstLoad]);
}; };
export default useGPUColumns; export default useGPUColumns;
@@ -239,10 +239,7 @@ const useWorkerColumns = ({
}; };
loadend: boolean; loadend: boolean;
firstLoad: boolean; firstLoad: boolean;
sortOrder: { sortOrder: string[];
order?: 'ascend' | 'descend' | null;
columnKey?: string;
};
handleSelect: (action: string, record: ListItem) => void; handleSelect: (action: string, record: ListItem) => void;
}): ColumnsType<ListItem> => { }): ColumnsType<ListItem> => {
const intl = useIntl(); const intl = useIntl();
@@ -277,11 +274,9 @@ const useWorkerColumns = ({
title: intl.formatMessage({ id: 'common.table.name' }), title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'name', dataIndex: 'name',
width: 100, width: 100,
sorter: false, sorter: {
sortOrder: multiple: 1
sortOrder.order && sortOrder.columnKey === 'name' },
? sortOrder.order
: null,
render: (text: string) => ( render: (text: string) => (
<AutoTooltip ghost maxWidth={240}> <AutoTooltip ghost maxWidth={240}>
{text} {text}
@@ -306,6 +301,9 @@ const useWorkerColumns = ({
{ {
title: intl.formatMessage({ id: 'common.table.status' }), title: intl.formatMessage({ id: 'common.table.status' }),
dataIndex: 'state', dataIndex: 'state',
sorter: {
multiple: 2
},
render: (_, record) => ( render: (_, record) => (
<StatusTag <StatusTag
maxTooltipWidth={400} maxTooltipWidth={400}
@@ -321,6 +319,9 @@ const useWorkerColumns = ({
{ {
title: 'IP', title: 'IP',
dataIndex: 'ip', dataIndex: 'ip',
sorter: {
multiple: 3
},
render: (text: string, record) => ( render: (text: string, record) => (
<AutoTooltip ghost maxWidth={240}> <AutoTooltip ghost maxWidth={240}>
{renderIP(text, record)} {renderIP(text, record)}
@@ -329,7 +330,10 @@ const useWorkerColumns = ({
}, },
{ {
title: 'CPU', title: 'CPU',
dataIndex: 'cpu', dataIndex: 'status.cpu.utilization_rate',
sorter: {
multiple: 4
},
render: (text: string, record) => render: (text: string, record) =>
statusAvailable(record) ? ( statusAvailable(record) ? (
<ProgressBar <ProgressBar
@@ -341,7 +345,10 @@ const useWorkerColumns = ({
}, },
{ {
title: intl.formatMessage({ id: 'resources.table.memory' }), title: intl.formatMessage({ id: 'resources.table.memory' }),
dataIndex: 'memory', dataIndex: 'status.memory.utilization_rate',
sorter: {
multiple: 5
},
render: (_, record) => render: (_, record) =>
statusAvailable(record) ? ( statusAvailable(record) ? (
<ProgressBar <ProgressBar
+17 -13
View File
@@ -71,12 +71,9 @@ const useUsersColumns = ({
title: intl.formatMessage({ id: 'common.table.name' }), title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'username', dataIndex: 'username',
key: 'username', key: 'username',
defaultSortOrder: 'descend', sorter: {
sortOrder: multiple: 1
sortOrder.order && sortOrder.columnKey === 'username' },
? sortOrder.order
: null,
sorter: false,
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<AutoTooltip ghost style={{ maxWidth: 400 }}> <AutoTooltip ghost style={{ maxWidth: 400 }}>
{text} {text}
@@ -85,8 +82,11 @@ const useUsersColumns = ({
}, },
{ {
title: intl.formatMessage({ id: 'users.table.role' }), title: intl.formatMessage({ id: 'users.table.role' }),
dataIndex: 'role', dataIndex: 'is_admin',
key: 'role', key: 'is_admin',
sorter: {
multiple: 6
},
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
@@ -125,6 +125,9 @@ const useUsersColumns = ({
title: intl.formatMessage({ id: 'users.form.source' }), title: intl.formatMessage({ id: 'users.form.source' }),
dataIndex: 'source', dataIndex: 'source',
key: 'source', key: 'source',
sorter: {
multiple: 3
},
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
@@ -138,6 +141,9 @@ const useUsersColumns = ({
title: intl.formatMessage({ id: 'users.table.status' }), title: intl.formatMessage({ id: 'users.table.status' }),
dataIndex: 'is_active', dataIndex: 'is_active',
key: 'is_active', key: 'is_active',
sorter: {
multiple: 4
},
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
@@ -185,11 +191,9 @@ const useUsersColumns = ({
key: 'created_at', key: 'created_at',
defaultSortOrder: 'descend', defaultSortOrder: 'descend',
showSorterTooltip: false, showSorterTooltip: false,
sortOrder: sorter: {
sortOrder.order && sortOrder.columnKey === 'created_at' multiple: 5
? sortOrder.order },
: null,
sorter: false,
ellipsis: { ellipsis: {
showTitle: false showTitle: false
}, },
+6 -2
View File
@@ -2,6 +2,7 @@ import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font'; import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools'; import { FilterBar } from '@/components/page-tools';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import type { PageActionType } from '@/config/types'; import type { PageActionType } from '@/config/types';
import useTableFetch from '@/hooks/use-table-fetch'; import useTableFetch from '@/hooks/use-table-fetch';
import { useIntl, useModel } from '@umijs/max'; import { useIntl, useModel } from '@umijs/max';
@@ -38,7 +39,10 @@ const Users: React.FC = () => {
} = useTableFetch<ListItem>({ } = useTableFetch<ListItem>({
fetchAPI: queryUsersList, fetchAPI: queryUsersList,
deleteAPI: deleteUser, deleteAPI: deleteUser,
contentForDelete: 'users.table.user' contentForDelete: 'users.table.user',
defaultQueryParams: {
sort_by: '-created_at'
}
}); });
const { initialState } = useModel('@@initialState') || {}; const { initialState } = useModel('@@initialState') || {};
@@ -188,7 +192,7 @@ const Users: React.FC = () => {
dataSource={dataList} dataSource={dataList}
rowSelection={rowSelection} rowSelection={rowSelection}
loading={dataSource.loading} loading={dataSource.loading}
sortDirections={['ascend', 'descend', 'ascend']} sortDirections={TABLE_SORT_DIRECTIONS}
rowKey="id" rowKey="id"
onChange={handleTableChange} onChange={handleTableChange}
pagination={{ pagination={{