feat: add resource for first login

This commit is contained in:
jialin
2025-12-11 17:50:39 +08:00
parent b38bc8a207
commit 3dbb0de683
11 changed files with 227 additions and 82 deletions
@@ -83,7 +83,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
handleOnValuesChange,
warningStatus
} = useCheckCompatibility();
const { getClusterList, clusterList } = useFormInitialValues();
const { getClusterList, getWorkerList, clusterList } = useFormInitialValues();
const intl = useIntl();
const form = useRef<any>({});
const [isGGUF, setIsGGUF] = useState<boolean>(false);
@@ -295,6 +295,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
useEffect(() => {
getClusterList();
getWorkerList();
}, []);
useEffect(() => {
+27 -28
View File
@@ -2,7 +2,6 @@ import { modelsExpandKeysAtom, modelsSessionAtom } from '@/atoms/models';
import DeleteModal from '@/components/delete-modal';
import DropDownActions from '@/components/drop-down-actions';
import DropdownButtons from '@/components/drop-down-buttons';
import IconFont from '@/components/icon-font';
import { PageSize } from '@/components/logs-viewer/config';
import PageTools from '@/components/page-tools';
import BaseSelect from '@/components/seal-form/base/select';
@@ -12,9 +11,8 @@ import { PageActionType } from '@/config/types';
import useBodyScroll from '@/hooks/use-body-scroll';
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
import useTableRowSelection from '@/hooks/use-table-row-selection';
import NoResult from '@/pages/_components/no-result';
import PageBox from '@/pages/_components/page-box';
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result';
import { handleBatchRequest } from '@/utils';
import { DownOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons';
import { useIntl, useNavigate } from '@umijs/max';
@@ -84,7 +82,6 @@ interface ModelsProps {
categories?: string[];
};
deleteIds?: number[];
workerList: WorkerListItem[];
dataSource: ListItem[];
loading: boolean;
loadend: boolean;
@@ -119,14 +116,18 @@ const Models: React.FC<ModelsProps> = ({
onStart,
deleteIds,
dataSource,
workerList,
queryParams,
loading,
loadend,
total
}) => {
const { generateFormValues, clusterList, getClusterList } =
useFormInitialValues();
const {
generateFormValues,
clusterList,
getClusterList,
getWorkerList,
workerList
} = useFormInitialValues();
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
const [updateFormInitials, setUpdateFormInitials] = useState<{
data: any;
@@ -206,7 +207,7 @@ const Models: React.FC<ModelsProps> = ({
useEffect(() => {
const getData = async () => {
await getClusterList();
await Promise.all([getClusterList(), getWorkerList()]);
};
getData();
return () => {
@@ -594,6 +595,23 @@ const Models: React.FC<ModelsProps> = ({
});
};
const { noResourceResult } = useNoResourceResult({
loadend: loadend,
loading: loading,
dataSource: dataSource,
queryParams: queryParams,
iconType: 'icon-resources',
title: intl.formatMessage({ id: 'noresult.deployments.title' }),
noClusters: !clusterList.length,
noWorkers: workerList.length === 0 && clusterList.length > 0,
defaultContent: {
subTitle: intl.formatMessage({ id: 'noresult.deployments.subTitle' }),
noFoundText: intl.formatMessage({ id: 'noresult.mymodels.nofound' }),
buttonText: intl.formatMessage({ id: 'models.table.button.deploy' }),
onClick: () => handleClickDropdown({ key: 'catalog' })
}
});
useEffect(() => {
if (modelsSession.source && loadend) {
handleClickDropdown({
@@ -709,26 +727,7 @@ const Models: React.FC<ModelsProps> = ({
loadChildren={getModelInstances}
loadChildrenAPI={generateChildrenRequestAPI}
renderChildren={renderChildren}
empty={
<NoResult
loading={loading}
loadend={loadend}
dataSource={dataSource}
image={<IconFont type="icon-rocket-launch1" />}
filters={queryParams}
noFoundText={intl.formatMessage({
id: 'noresult.mymodels.nofound'
})}
title={intl.formatMessage({ id: 'noresult.deployments.title' })}
subTitle={intl.formatMessage({
id: 'noresult.deployments.subTitle'
})}
onClick={() => handleClickDropdown({ key: 'catalog' })}
buttonText={intl?.formatMessage?.({
id: 'models.table.button.deploy'
})}
></NoResult>
}
empty={noResourceResult}
pagination={{
showSizeChanger: true,
pageSize: queryParams.perPage,
+7 -3
View File
@@ -1,4 +1,4 @@
import { clusterListAtom } from '@/atoms/models';
import { clusterListAtom, workerListAtom } from '@/atoms/models';
import { createAxiosToken } from '@/hooks/use-chunk-request';
import { queryModelFilesList } from '@/pages/resources/apis';
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
@@ -132,6 +132,7 @@ export const useCheckCompatibility = () => {
const updateStatusTimer = useRef<any>(null);
const isLockWarningStatus = useRef<boolean>(false);
const clusterList = useAtomValue(clusterListAtom);
const workerList = useAtomValue(workerListAtom);
const [warningStatus, setWarningStatus] = useState<MessageStatus>({
show: false,
title: '',
@@ -175,12 +176,15 @@ export const useCheckCompatibility = () => {
try {
// when no cluster selected, show warning and prompt user to add cluster first
console.log('handleEvaluate', data);
if (!data.cluster_id) {
if (!data.cluster_id || workerList.length === 0) {
setWarningStatus({
show: true,
title: '',
type: 'warning',
message: intl.formatMessage({ id: 'noresult.resources.cluster' })
message: !data.cluster_id
? intl.formatMessage({ id: 'noresult.resources.cluster' })
: intl.formatMessage({ id: 'noresult.resources.worker' })
});
return;
}
@@ -1,4 +1,4 @@
import { clusterListAtom } from '@/atoms/models';
import { clusterListAtom, workerListAtom } from '@/atoms/models';
import { queryClusterList } from '@/pages/cluster-management/apis';
import { ClusterListItem } from '@/pages/cluster-management/config/types';
import { queryWorkersList } from '@/pages/resources/apis';
@@ -232,11 +232,14 @@ export const useGenerateWorkerOptions = () => {
export default function useFormInitialValues() {
const { getGPUOptionList, gpuOptions } = useGenerateGPUOptions();
const [, setClusterListAtom] = useAtom(clusterListAtom);
const [, setWorkerListAtom] = useAtom(workerListAtom);
const [clusterList, setClusterList] = useState<
Global.BaseOption<number, { provider: string; state: string }>[]
>([]);
const [workerList, setWorkerList] = useState<WorkerListItem[]>([]);
const getClusterList = async (): Promise<Global.BaseOption<number>[]> => {
try {
const response = await queryClusterList({
@@ -259,6 +262,26 @@ export default function useFormInitialValues() {
}
};
// get worker list
const getWorkerList = async (): Promise<any> => {
try {
const data = await queryWorkersList({ page: -1 });
const list =
data.items?.map((item) => ({
label: item.name,
value: item.id
})) || [];
setWorkerList(data.items);
setWorkerListAtom(list);
return data;
} catch (error) {
// ingore
setWorkerList([]);
setWorkerListAtom([]);
return {};
}
};
/**
* before set the form initial values, generate the form values
* @param data
@@ -280,6 +303,8 @@ export default function useFormInitialValues() {
getGPUOptionList,
generateFormValues,
getClusterList,
getWorkerList,
workerList,
clusterList,
gpuOptions
};
@@ -7,6 +7,12 @@ import { useMemoizedFn } from 'ahooks';
import { useAtom } from 'jotai';
import React, { useMemo } from 'react';
/**Title: Generally, this is from the activation page.
* DefaultContent: This content from the activate page, for example, the activate page is Deployments page,
* then the defaultContent is for deployment.
* @param props
* @returns
*/
const useNoResourceResult = (props: {
iconType: string;
loading?: boolean;
@@ -84,6 +90,7 @@ const useNoResourceResult = (props: {
onClick: handleClick
};
}
return {
...defaultContent
};
+1 -20
View File
@@ -1,8 +1,6 @@
import TableContext from '@/components/seal-table/table-context';
import useSetChunkRequest from '@/hooks/use-chunk-request';
import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
import { queryWorkersList } from '@/pages/resources/apis';
import { ListItem as WokerListItem } from '@/pages/resources/config/types';
import { useMemoizedFn } from 'ahooks';
import _ from 'lodash';
import qs from 'query-string';
@@ -35,7 +33,6 @@ const Models: React.FC = () => {
total: 0
});
const [workerList, setWorkerList] = useState<WokerListItem[]>([]);
const chunkRequedtRef = useRef<any>();
const chunkInstanceRequedtRef = useRef<any>();
const isPageHidden = useRef(false);
@@ -320,22 +317,8 @@ const Models: React.FC = () => {
}
};
// get worker list
const getWorkerList = async (): Promise<any> => {
try {
const data = await queryWorkersList({ page: -1 });
return data;
} catch (error) {
// ingore
return {};
}
};
const init = async () => {
const [modelRes, workerRes] = await Promise.all([
getTableData(),
getWorkerList()
]);
const [modelRes] = await Promise.all([getTableData()]);
setDataSource({
dataList: modelRes.items || [],
@@ -344,7 +327,6 @@ const Models: React.FC = () => {
total: modelRes.pagination?.total || 0,
deletedIds: []
});
setWorkerList(workerRes.items || []);
clearTimeout(timer);
timer = setTimeout(() => {
@@ -420,7 +402,6 @@ const Models: React.FC = () => {
loadend={dataSource.loadend}
total={dataSource.total}
deleteIds={dataSource.deletedIds}
workerList={workerList}
></TableList>
</TableContext.Provider>
);