diff --git a/src/components/card-wrapper/index.less b/src/components/card-wrapper/index.less
deleted file mode 100644
index 6376d7e8..00000000
--- a/src/components/card-wrapper/index.less
+++ /dev/null
@@ -1,7 +0,0 @@
-.cardWrapper {
- border-radius: var(--border-radius-small);
- background-color: var(--ant-color-bg-container);
- box-shadow: none;
- padding: 10px 16px;
- border: 1px solid var(--ant-color-border);
-}
diff --git a/src/components/card-wrapper/index.tsx b/src/components/card-wrapper/index.tsx
index 90c076b3..99d72401 100644
--- a/src/components/card-wrapper/index.tsx
+++ b/src/components/card-wrapper/index.tsx
@@ -1,12 +1,16 @@
-import styles from './index.less';
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+ border-radius: var(--border-radius-small);
+ background-color: var(--ant-color-bg-container);
+ box-shadow: none;
+ padding: 10px 16px;
+ border: 1px solid var(--ant-color-border);
+`;
const CardWrapper = (props: any) => {
const { children, style } = props;
- return (
-
- {children}
-
- );
+ return {children};
};
export default CardWrapper;
diff --git a/src/config/theme/dark.ts b/src/config/theme/dark.ts
index 79d802aa..34e8120d 100644
--- a/src/config/theme/dark.ts
+++ b/src/config/theme/dark.ts
@@ -84,6 +84,9 @@ export default {
trackHoverBg: '#646464',
dotActiveBorderColor: 'rgba(255,255,255,0.25)',
dotBorderColor: 'rgba(255,255,255,0.25)'
+ },
+ Descriptions: {
+ itemPaddingBottom: 8
}
},
token: {
diff --git a/src/config/theme/light.ts b/src/config/theme/light.ts
index ed2bedfd..dfd98922 100644
--- a/src/config/theme/light.ts
+++ b/src/config/theme/light.ts
@@ -83,6 +83,9 @@ export default {
trackHoverBg: '#B4B4B4',
dotActiveBorderColor: 'rgba(0,0,0,0.25)',
dotBorderColor: 'rgba(0,0,0,0.25)'
+ },
+ Descriptions: {
+ itemPaddingBottom: 8
}
},
token: {
diff --git a/src/pages/cluster-management/apis/index.ts b/src/pages/cluster-management/apis/index.ts
index 29d1fee5..39317f9b 100644
--- a/src/pages/cluster-management/apis/index.ts
+++ b/src/pages/cluster-management/apis/index.ts
@@ -149,6 +149,13 @@ export async function queryClusterDetail(
});
}
+export async function queryClusterItem(params: { id: number }, options?: any) {
+ return request(`${CLUSTERS_API}/${params.id}`, {
+ method: 'GET',
+ cancelToken: options?.token
+ });
+}
+
export async function queryClusterToken(params: { id: number }) {
return request(`${CLUSTERS_API}/${params.id}/${CLUSTER_TOKEN}`, {
method: 'GET'
diff --git a/src/pages/cluster-management/cluster-detail.tsx b/src/pages/cluster-management/cluster-detail.tsx
index 6f17616f..07131235 100644
--- a/src/pages/cluster-management/cluster-detail.tsx
+++ b/src/pages/cluster-management/cluster-detail.tsx
@@ -1,7 +1,10 @@
+import IconFont from '@/components/icon-font';
import WorkerList from '@/pages/resources/components/workers';
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
+import { Tabs } from 'antd';
import { PageContainerInner } from '../_components/page-box';
import PageBreadcrumb from '../_components/page-breadcrumb';
+import ClusterBasic from './components/detail/cluster-basic';
import ClusterSystemLoad from './components/detail/cluster-system-load';
const ClusterDetailModal = () => {
@@ -27,13 +30,40 @@ const ClusterDetailModal = () => {
title:
}}
>
+
- ,
+ children: (
+
+ )
+ },
+ {
+ key: 'deployments',
+ label: 'Deployments',
+ icon: ,
+ children: Deployments Content
+ },
+ {
+ key: 'gpus',
+ label: 'GPUs',
+ icon: ,
+ children: GPUs Content
+ }
+ ]}
/>
);
diff --git a/src/pages/cluster-management/components/detail/cluster-basic.tsx b/src/pages/cluster-management/components/detail/cluster-basic.tsx
new file mode 100644
index 00000000..f7f86e5e
--- /dev/null
+++ b/src/pages/cluster-management/components/detail/cluster-basic.tsx
@@ -0,0 +1,167 @@
+import CardWrapper from '@/components/card-wrapper';
+import IconFont from '@/components/icon-font';
+import StatusTag from '@/components/status-tag';
+import { StarFilled } from '@ant-design/icons';
+import { useIntl } from '@umijs/max';
+import type { DescriptionsProps } from 'antd';
+import { Descriptions, Tooltip } from 'antd';
+import dayjs from 'dayjs';
+import React, { useEffect } from 'react';
+import styled from 'styled-components';
+import {
+ ClusterStatus,
+ ClusterStatusLabelMap,
+ ProviderLabelMap
+} from '../../config';
+import providers from '../../config/providers';
+import { useClusterDetail } from '../../services/use-cluster-detail';
+
+const Container = styled.div`
+ display: flex;
+ height: 184px;
+ .left {
+ padding: 24px 24px;
+ width: 160px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 20px;
+ .img {
+ width: 72px;
+ height: 72px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 8px;
+ background-color: var(--ant-blue-1);
+ .anticon {
+ font-size: 36px;
+ }
+ }
+ }
+ .right {
+ flex: 1;
+ padding-block: 24px;
+ padding-inline: 0 24px;
+ }
+`;
+
+const Title = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 8px;
+`;
+
+const Resources = styled.div`
+ display: flex;
+ gap: 24px;
+ align-items: center;
+ line-height: 22px;
+ margin-top: 16px;
+ .item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ .anticon {
+ color: var(--ant-color-text-quaternary);
+ font-size: 16px;
+ }
+ .value {
+ font-weight: 500;
+ }
+ .label {
+ color: var(--ant-color-text-secondary);
+ }
+ }
+`;
+
+const ClusterBasic: React.FC<{ clusterId: number }> = ({ clusterId }) => {
+ const intl = useIntl();
+ const { clusterDetail, fetchClusterDetail } = useClusterDetail();
+
+ const items: DescriptionsProps['items'] = [
+ {
+ key: 'provider',
+ label: intl.formatMessage({ id: 'clusters.table.provider' }),
+ children: ProviderLabelMap[clusterDetail.provider || '']
+ },
+ {
+ key: 'created_at',
+ label: intl.formatMessage({ id: 'common.table.createTime' }),
+ children: dayjs(clusterDetail.created_at).format('YYYY-MM-DD HH:mm:ss')
+ }
+ ];
+
+ useEffect(() => {
+ if (clusterId) {
+ fetchClusterDetail({ id: clusterId });
+ }
+ }, [clusterId]);
+
+ return (
+
+
+
+
+ {
+ providers.find((item) => item.value === clusterDetail?.provider)
+ ?.icon
+ }
+
+
+
+
+
+
+
+ {clusterDetail.name}
+ {clusterDetail.is_default && (
+
+
+
+ )}
+
+ }
+ layout="vertical"
+ items={items}
+ />
+
+
+
+
+ {clusterDetail.ready_workers}/{clusterDetail.workers}
+
+ Workers
+
+
+
+ {clusterDetail.models}
+ Deployments
+
+
+
+ {clusterDetail.gpus}
+ GPUs
+
+
+
+
+
+ );
+};
+
+export default ClusterBasic;
diff --git a/src/pages/cluster-management/components/detail/cluster-system-load.tsx b/src/pages/cluster-management/components/detail/cluster-system-load.tsx
index 472f5548..d42fc8cf 100644
--- a/src/pages/cluster-management/components/detail/cluster-system-load.tsx
+++ b/src/pages/cluster-management/components/detail/cluster-system-load.tsx
@@ -1,5 +1,36 @@
+import CardWrapper from '@/components/card-wrapper';
+import { Col, Progress, Row } from 'antd';
import React, { useEffect } from 'react';
-import { useClusterSystemLoad } from '../../services/use-cluster-system-load';
+import styled from 'styled-components';
+import { useClusterSystemLoad } from '../../services/use-cluster-detail';
+
+const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ height: 86px;
+ gap: 16px;
+ .title {
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--ant-color-text-secondary);
+ }
+
+ .value-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ .value {
+ display: flex;
+ flex: 1;
+ gap: 8px;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+ font-size: 20px;
+ font-weight: 600;
+ }
+ }
+`;
const ClusterSystemLoad: React.FC<{ clusterId: number }> = ({ clusterId }) => {
const { systemLoad, fetchClusterSystemLoad } = useClusterSystemLoad();
@@ -10,7 +41,98 @@ const ClusterSystemLoad: React.FC<{ clusterId: number }> = ({ clusterId }) => {
}
}, [clusterId]);
- return Cluster System Load Component
;
+ return (
+
+
+
+
+ GPU Utilization
+
+
+
+
+
+
+
+ VRAM Utilization
+
+
+
+
+
+
+
+ CPU Utilization
+
+
+
+
+
+
+
+ Memo Utilization
+
+
+
+
+
+ );
};
export default ClusterSystemLoad;
diff --git a/src/pages/cluster-management/config/types.ts b/src/pages/cluster-management/config/types.ts
index 2d8acf15..da8def7a 100644
--- a/src/pages/cluster-management/config/types.ts
+++ b/src/pages/cluster-management/config/types.ts
@@ -57,6 +57,7 @@ export interface ClusterListItem {
worker_config: Record;
provider: ProviderType;
credential_id: number;
+ created_at: string;
zone: string;
region: string;
gpus: number;
diff --git a/src/pages/cluster-management/hooks/use-cluster-columns.tsx b/src/pages/cluster-management/hooks/use-cluster-columns.tsx
index b5d1b49e..bc5a8399 100644
--- a/src/pages/cluster-management/hooks/use-cluster-columns.tsx
+++ b/src/pages/cluster-management/hooks/use-cluster-columns.tsx
@@ -6,7 +6,7 @@ import StatusTag from '@/components/status-tag';
import { tableSorter } from '@/config/settings';
import { StarFilled } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
-import { Tooltip, Typography } from 'antd';
+import { Tooltip } from 'antd';
import dayjs from 'dayjs';
import { useMemo } from 'react';
import {
@@ -42,9 +42,10 @@ const useClusterColumns = (
render: (text: string, record: ClusterListItem) => (
<>
- onCellClick?.(record, 'name')}>
+ {/* onCellClick?.(record, 'name')}>
{record.name}
-
+ */}
+ {record.name}
{record.is_default && (
{
+ const axiosTokenRef = useRef(null);
const [systemLoad, setSystemLoad] = useState<{
current: {
cpu: number;
@@ -22,7 +24,6 @@ export const useClusterSystemLoad = () => {
},
history: {}
});
- const axiosTokenRef = useRef(null);
const {
run: fetchClusterSystemLoad,
@@ -71,3 +72,46 @@ export const useClusterSystemLoad = () => {
fetchClusterSystemLoad
};
};
+
+export const useClusterDetail = () => {
+ const axiosTokenRef = useRef(null);
+ const [clusterDetail, setClusterDetail] = useState(
+ {} as ClusterListItem
+ );
+
+ const {
+ run: fetchClusterDetail,
+ loading,
+ cancel
+ } = useRequest(
+ async (params) => {
+ axiosTokenRef.current?.cancel();
+ axiosTokenRef.current = createAxiosToken();
+ return await queryClusterItem(params, {
+ token: axiosTokenRef.current.token
+ });
+ },
+ {
+ manual: true,
+ onSuccess: (response) => {
+ setClusterDetail(response);
+ },
+ onError: () => {
+ setClusterDetail({} as ClusterListItem);
+ }
+ }
+ );
+
+ useEffect(() => {
+ return () => {
+ cancel();
+ axiosTokenRef.current?.cancel();
+ };
+ }, []);
+
+ return {
+ clusterDetail,
+ loading,
+ fetchClusterDetail
+ };
+};
diff --git a/src/pages/llmodels/components/catalog-item.tsx b/src/pages/llmodels/components/catalog-item.tsx
index c0f9dcd8..fa215f97 100644
--- a/src/pages/llmodels/components/catalog-item.tsx
+++ b/src/pages/llmodels/components/catalog-item.tsx
@@ -129,15 +129,17 @@ const CatalogItem: React.FC = (props) => {
);
})}
-
- {data.activated_size
- ? `${data.size}B-A${data.activated_size}B`
- : `${data.size}B`}
-
+ {data.size > 0 && (
+
+ {data.activated_size
+ ? `${data.size}${data.size_unit}-A${data.activated_size}${data.size_unit}`
+ : `${data.size}${data.size_unit}`}
+
+ )}
diff --git a/src/pages/llmodels/config/types.ts b/src/pages/llmodels/config/types.ts
index 0cdd8ce0..01566aa3 100644
--- a/src/pages/llmodels/config/types.ts
+++ b/src/pages/llmodels/config/types.ts
@@ -191,6 +191,7 @@ export interface CatalogItem {
categories: string[];
capabilities: string[];
size: number;
+ size_unit: string;
activated_size: number;
licenses: string[];
release_date: string;