diff --git a/src/components/seal-table/components/header-prefix.tsx b/src/components/seal-table/components/header-prefix.tsx index a8c4683a..0a979ebe 100644 --- a/src/components/seal-table/components/header-prefix.tsx +++ b/src/components/seal-table/components/header-prefix.tsx @@ -9,11 +9,21 @@ interface HeaderPrefixProps { onSelectAll?: (e: any) => void; indeterminate?: boolean; selectAll?: boolean; + hasColumns?: boolean; } const HeaderPrefix: React.FC = (props) => { - const { expandable, enableSelection, onSelectAll, indeterminate, selectAll } = - props; + const { + hasColumns, + expandable, + enableSelection, + onSelectAll, + indeterminate, + selectAll + } = props; + if (!hasColumns) { + return null; + } if (expandable && enableSelection) { return (
diff --git a/src/components/seal-table/components/table-body.tsx b/src/components/seal-table/components/table-body.tsx index 6da6f5da..7e3f67b4 100644 --- a/src/components/seal-table/components/table-body.tsx +++ b/src/components/seal-table/components/table-body.tsx @@ -18,6 +18,7 @@ interface TableBodyProps { loadChildren?: any; loadChildrenAPI?: any; onCell?: any; + empty?: React.ReactNode; } const TableBody: React.FC = ({ @@ -34,12 +35,13 @@ const TableBody: React.FC = ({ loadChildren, loadChildrenAPI, columns, - onCell + onCell, + empty }) => { if (!dataSource.length) { return (
- + {empty || }
); } diff --git a/src/components/seal-table/index.tsx b/src/components/seal-table/index.tsx index a7d6f679..12fdeada 100644 --- a/src/components/seal-table/index.tsx +++ b/src/components/seal-table/index.tsx @@ -26,6 +26,7 @@ const SealTable: React.FC = ( watchChildren, rowSelection, pagination, + empty, renderChildren, loadChildren, loadChildrenAPI @@ -116,11 +117,13 @@ const SealTable: React.FC = ( onSelectAll={handleSelectAllChange} expandable={expandable} enableSelection={rowSelection?.enableSelection} + hasColumns={parsedColumns.length > 0} >
= ({ dataSource, gpuDeviceList, workerList, + catalogList, queryParams, loading, loadend, total }) => { const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); + const [isFirstLogin, setIsFirstLogin] = useState(false); + const [isLoading, setIsLoading] = useState(false); const [expandAtom, setExpandAtom] = useAtom(modelsExpandKeysAtom); const access = useAccess(); const intl = useIntl(); @@ -199,6 +219,17 @@ const Models: React.FC = ({ }); const modalRef = useRef(null); + useEffect(() => { + if (!catalogList?.length) { + return; + } + const getFirstLoginState = async () => { + const is_first_login = await readState(IS_FIRST_LOGIN); + setIsFirstLogin(is_first_login); + }; + getFirstLoginState(); + }, [catalogList?.length]); + useHotkeys( HotKeys.NEW1.join(','), () => { @@ -667,8 +698,11 @@ const Models: React.FC = ({ }); }; - const columns: SealColumnProps[] = useMemo( - () => [ + const columns: SealColumnProps[] = useMemo(() => { + if (isFirstLogin) { + return []; + } + return [ { title: intl.formatMessage({ id: 'common.table.name' }), dataIndex: 'name', @@ -744,13 +778,67 @@ const Models: React.FC = ({ /> ) } - ], - [sortOrder, intl] - ); + ]; + }, [sortOrder, intl, isFirstLogin]); + + const handleOnClick = async () => { + if (isLoading) { + return; + } + + const data = catalogList?.find( + (item) => item.backend === backendOptionsMap.llamaBox && item.size === 1.5 + ); + console.log('catalogList=======', data); + try { + if (data) { + setIsLoading(true); + const modelData = await createModel({ + data: data + }); + writeState(IS_FIRST_LOGIN, false); + setIsFirstLogin(false); + setTimeout(() => { + updateExpandedRowKeys([modelData.id]); + }, 300); + message.success(intl.formatMessage({ id: 'common.message.success' })); + handleSearch?.(); + } + } catch (error) { + // ingore + } finally { + setIsLoading(false); + } + }; + const renderEmpty = useMemo(() => { + if (dataSource.length || !isFirstLogin) { + return null; + } + return ( +
+ + + No Models yet! + +
+ +
+
+ ); + }, [dataSource.length, isFirstLogin, isLoading]); return ( <> = ({ > = ({ } > + { total: 0 }); + const [catalogList, setCatalogList] = useState([]); const [gpuDeviceList, setGpuDeviceList] = useState([]); const [workerList, setWorkerList] = useState([]); const chunkRequedtRef = useRef(); @@ -243,6 +247,33 @@ const Models: React.FC = () => { }; }, [queryParams]); + useEffect(() => { + const getCataLogList = async () => { + const isFirstLogin = readState(IS_FIRST_LOGIN); + if (!isFirstLogin) { + return; + } + try { + const res: any = await queryCatalogList({ + search: 'DeepSeek R1', + page: 1 + }); + const name = _.toLower(res?.items[0]?.name).replace(/\s/g, '-') || ''; + const catalogSpecs: any = await queryCatalogItemSpec({ + id: res?.items[0]?.id + }); + const list = catalogSpecs?.items?.map((item: any) => { + item.name = name; + return item; + }); + setCatalogList(list || []); + } catch (error) { + // ignore + } + }; + getCataLogList(); + }, []); + useEffect(() => { createModelsChunkRequest(); }, [createModelsChunkRequest]); @@ -311,6 +342,7 @@ const Models: React.FC = () => { deleteIds={dataSource.deletedIds} gpuDeviceList={gpuDeviceList} workerList={workerList} + catalogList={catalogList} > ); diff --git a/src/pages/login/components/login-form.tsx b/src/pages/login/components/login-form.tsx index 8e337ef5..004cd20e 100644 --- a/src/pages/login/components/login-form.tsx +++ b/src/pages/login/components/login-form.tsx @@ -8,13 +8,14 @@ import { removeRememberMe } from '@/utils/localstore/index'; import { LockOutlined, UserOutlined } from '@ant-design/icons'; -import { history, useIntl, useModel } from '@umijs/max'; +import { useIntl, useModel } from '@umijs/max'; import { Button, Checkbox, Form } from 'antd'; import CryptoJS from 'crypto-js'; import { useAtom } from 'jotai'; import { memo, useEffect, useMemo } from 'react'; import { flushSync } from 'react-dom'; import { login } from '../apis'; +import { checkDefaultPage } from '../utils'; const REMEMBER_ME_KEY = 'r_m'; const CRYPT_TEXT = 'seal'; @@ -50,11 +51,10 @@ const LoginForm = () => { ); }, [intl]); - const gotoDefaultPage = (userInfo: any) => { - const pathname = - userInfo && userInfo?.is_admin ? '/dashboard' : '/playground'; - history.push(pathname); + const gotoDefaultPage = async (userInfo: any) => { + checkDefaultPage(userInfo, true); }; + const fetchUserInfo = async () => { const userInfo = await initialState?.fetchUserInfo?.(); @@ -108,6 +108,7 @@ const LoginForm = () => { username: values.username, password: values.password }); + const userInfo = await fetchUserInfo(); setUserInfo(userInfo); if (values.autoLogin) { diff --git a/src/pages/login/components/password-form.tsx b/src/pages/login/components/password-form.tsx index 6507cebb..eaac4d3f 100644 --- a/src/pages/login/components/password-form.tsx +++ b/src/pages/login/components/password-form.tsx @@ -2,11 +2,12 @@ import { initialPasswordAtom, userAtom } from '@/atoms/user'; import SealInput from '@/components/seal-form/seal-input'; import { PasswordReg } from '@/config'; import { GlobalOutlined, LockOutlined } from '@ant-design/icons'; -import { SelectLang, history, useIntl } from '@umijs/max'; +import { SelectLang, useIntl } from '@umijs/max'; import { Button, Form, message } from 'antd'; import CryptoJS from 'crypto-js'; import { useAtom } from 'jotai'; import { updatePassword } from '../apis'; +import { checkDefaultPage } from '../utils'; const CRYPT_TEXT = 'seal'; @@ -16,10 +17,8 @@ const PasswordForm: React.FC = () => { const [userInfo, setUserInfo] = useAtom(userAtom); const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom); - const gotoDefaultPage = (userInfo: any) => { - const pathname = - userInfo && userInfo?.is_admin ? '/dashboard' : '/playground'; - history.push(pathname); + const gotoDefaultPage = async (userInfo: any) => { + checkDefaultPage(userInfo, false); }; const decryptPassword = (password: string) => { diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index c5f9eb92..3ac14df5 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -1,29 +1,29 @@ import { userAtom } from '@/atoms/user'; import Footer from '@/components/footer'; -import { history, useModel } from '@umijs/max'; +import { useModel } from '@umijs/max'; import { useAtom } from 'jotai'; import { useEffect } from 'react'; import LoginForm from './components/login-form'; import PasswordForm from './components/password-form'; import styles from './components/styles.less'; +import { checkDefaultPage } from './utils'; const Login = () => { const [userInfo, setUserInfo] = useAtom(userAtom); const { initialState, setInitialState } = useModel('@@initialState') || {}; - const gotoDefaultPage = (userInfo: any) => { + const gotoDefaultPage = async (userInfo: any) => { if (!userInfo || userInfo?.require_password_change) { return; } + + checkDefaultPage(userInfo, true); if (!initialState?.currentUser) { setInitialState((s: any) => ({ ...s, currentUser: userInfo })); } - const pathname = userInfo?.is_admin ? '/dashboard' : '/playground'; - - history.push(pathname, { replace: true }); }; useEffect(() => { gotoDefaultPage(userInfo); diff --git a/src/pages/login/utils.ts b/src/pages/login/utils.ts new file mode 100644 index 00000000..43890c8c --- /dev/null +++ b/src/pages/login/utils.ts @@ -0,0 +1,24 @@ +import { isOnline } from '@/utils'; +import { + IS_FIRST_LOGIN, + readState, + writeState +} from '@/utils/localstore/index'; +import { history } from '@umijs/max'; + +export const checkDefaultPage = async (userInfo: any, replace: boolean) => { + const isFirstLogin = await readState(IS_FIRST_LOGIN); + + if (isFirstLogin === null && isOnline()) { + writeState(IS_FIRST_LOGIN, true); + const pathname = + userInfo && userInfo?.is_admin ? '/models/list' : '/playground'; + history.push(pathname); + return; + } + await writeState(IS_FIRST_LOGIN, false); + + const pathname = userInfo?.is_admin ? '/dashboard' : '/playground'; + + history.push(pathname, { replace: replace }); +}; diff --git a/src/utils/index.ts b/src/utils/index.ts index 17b31df2..ff63eca0 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -192,3 +192,8 @@ export const base64ToFile = (base64String: string, fileName: string) => { return null; } }; + +// check onlinestatus +export const isOnline = () => { + return window.navigator.onLine; +}; diff --git a/src/utils/localstore/index.ts b/src/utils/localstore/index.ts index 2a7c42e4..d857fc6b 100644 --- a/src/utils/localstore/index.ts +++ b/src/utils/localstore/index.ts @@ -1,5 +1,7 @@ import localStore from './store'; +const IS_FIRST_LOGIN = 'is_first_login'; + const store = localStore.createInstance({ name: '_xWXJKJ_S1Sna_' }); const rememberMe = (key: string, data: any) => { @@ -20,6 +22,25 @@ const removeRememberMe = (key: string) => { } }; -export { getRememberMe, rememberMe, removeRememberMe }; +const readState = (key: string) => { + if (store) { + return store.getItem(key); + } +}; + +const writeState = (key: string, data: any) => { + if (store) { + store.setItem(key, data); + } +}; + +export { + IS_FIRST_LOGIN, + getRememberMe, + readState, + rememberMe, + removeRememberMe, + writeState +}; export default store;