From d47e53114c9c9845869df3ab43c53123830c8cb6 Mon Sep 17 00:00:00 2001 From: jialin Date: Sat, 29 Jun 2024 19:54:40 +0800 Subject: [PATCH] chore: first login modify password --- config/config.ts | 1 - config/proxy.ts | 3 +- src/assets/styles/common.less | 5 ++ src/atoms/user.ts | 3 + src/atoms/utils/index.ts | 17 +++++ .../seal-table/components/table-row.tsx | 45 +++++++++++- src/components/seal-table/index.tsx | 6 +- src/components/seal-table/types.ts | 2 + src/components/util-bar/index.less | 17 +++++ src/components/util-bar/index.tsx | 52 ++++++++++++++ src/config/index.ts | 6 +- src/global.less | 1 + src/hooks/use-chunk-request.ts | 2 + src/hooks/use-event-source.ts | 13 +++- src/layouts/index.tsx | 17 ++++- src/locales/en-US/users.ts | 3 +- src/locales/zh-CN/users.ts | 3 +- .../dashboard/components/system-load.tsx | 38 ++++------ src/pages/llmodels/config/index.ts | 6 ++ src/pages/llmodels/index.tsx | 68 +++++++++++++----- src/pages/login/apis/index.ts | 6 +- src/pages/login/components/login-form.tsx | 29 +++----- src/pages/login/components/password-form.tsx | 71 ++++++++++--------- src/pages/login/index.tsx | 23 ++++-- src/request-config.ts | 13 ++-- 25 files changed, 337 insertions(+), 113 deletions(-) create mode 100644 src/atoms/user.ts create mode 100644 src/atoms/utils/index.ts create mode 100644 src/components/util-bar/index.less create mode 100644 src/components/util-bar/index.tsx diff --git a/config/config.ts b/config/config.ts index 4cf6dd41..dfcaf3b0 100644 --- a/config/config.ts +++ b/config/config.ts @@ -98,7 +98,6 @@ export default defineConfig({ hash: true, access: {}, model: {}, - valtio: {}, initialState: {}, request: {}, locale: { diff --git a/config/proxy.ts b/config/proxy.ts index 4b4c43ea..6e5586c2 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -12,7 +12,8 @@ export default function createProxyTable(target?: string) { ws: true, pathRewrite: (pth: string) => pth.replace(`/^/${api}/`, `/${api}`), headers: { - origin: newTarget + origin: newTarget, + Connection: 'keep-alive' } }; return obj; diff --git a/src/assets/styles/common.less b/src/assets/styles/common.less index 95f06e8b..21f495fe 100644 --- a/src/assets/styles/common.less +++ b/src/assets/styles/common.less @@ -34,6 +34,11 @@ display: flex; justify-content: space-between; } + +.justify-center { + display: flex; + justify-content: center; +} // align-items: center .flex-center { display: flex; diff --git a/src/atoms/user.ts b/src/atoms/user.ts new file mode 100644 index 00000000..0aaedc4e --- /dev/null +++ b/src/atoms/user.ts @@ -0,0 +1,3 @@ +import { atomWithStorage } from 'jotai/utils'; + +export const userAtom = atomWithStorage('userInfo', null); diff --git a/src/atoms/utils/index.ts b/src/atoms/utils/index.ts new file mode 100644 index 00000000..1482f180 --- /dev/null +++ b/src/atoms/utils/index.ts @@ -0,0 +1,17 @@ +import { getDefaultStore } from 'jotai'; + +export const clearAtomStorage = (atom: any) => { + if (!atom) { + return; + } + const store = getDefaultStore(); + store.set(atom, null); +}; + +export const getAtomStorage = (atom: any) => { + if (!atom) { + return null; + } + const store = getDefaultStore(); + return store.get(atom); +}; diff --git a/src/components/seal-table/components/table-row.tsx b/src/components/seal-table/components/table-row.tsx index f7e9f5e1..5409468b 100644 --- a/src/components/seal-table/components/table-row.tsx +++ b/src/components/seal-table/components/table-row.tsx @@ -1,3 +1,5 @@ +import useSetChunkRequest from '@/hooks/use-chunk-request'; +import useUpdateChunkedList from '@/hooks/use-update-chunk-list'; import { DownOutlined, RightOutlined } from '@ant-design/icons'; import { Button, Checkbox, Col, Empty, Row, Spin } from 'antd'; import classNames from 'classnames'; @@ -18,16 +20,23 @@ const TableRow: React.FC< rowKey, columns, pollingChildren, + watchChildren, onExpand, renderChildren, - loadChildren + loadChildren, + loadChildrenAPI } = props; - + const { setChunkRequest } = useSetChunkRequest(); const [expanded, setExpanded] = useState(false); const [checked, setChecked] = useState(false); const [childrenData, setChildrenData] = useState([]); const [loading, setLoading] = useState(false); const pollTimer = useRef(null); + const chunkRequestRef = useRef(null); + + const { updateChunkedList } = useUpdateChunkedList(childrenData, { + setDataList: setChildrenData + }); useEffect(() => { if (rowSelection) { @@ -45,6 +54,7 @@ const TableRow: React.FC< if (pollTimer.current) { clearInterval(pollTimer.current); } + chunkRequestRef.current?.current?.cancel?.(); }; }, []); @@ -71,6 +81,31 @@ const TableRow: React.FC< } }; + const updateChildrenHandler = (list: any) => { + _.each(list, (data: any) => { + updateChunkedList(data); + }); + }; + const createChunkRequest = () => { + chunkRequestRef.current?.current?.cancel?.(); + if (!watchChildren) { + return; + } + const url = loadChildrenAPI?.(record) as string; + try { + chunkRequestRef.current = setChunkRequest({ + url, + params: { + page: 1, + perPage: 100 + }, + handler: updateChildrenHandler + }); + } catch (error) { + // ignore + } + }; + const handleRowExpand = async () => { setExpanded(!expanded); onExpand?.(!expanded, record); @@ -80,6 +115,7 @@ const TableRow: React.FC< } if (expanded) { + chunkRequestRef.current?.current?.cancel?.(); return; } @@ -88,6 +124,9 @@ const TableRow: React.FC< pollTimer.current = setInterval(() => { handlePolling(); }, 1000); + } else if (watchChildren) { + await handleLoadChildren(); + createChunkRequest(); } else { handleLoadChildren(); } @@ -194,4 +233,4 @@ const TableRow: React.FC< ); }; -export default TableRow; +export default React.memo(TableRow); diff --git a/src/components/seal-table/index.tsx b/src/components/seal-table/index.tsx index 9f323eea..701ac839 100644 --- a/src/components/seal-table/index.tsx +++ b/src/components/seal-table/index.tsx @@ -15,9 +15,11 @@ const SealTable: React.FC = (props) => { loading, expandable, pollingChildren, + watchChildren, rowSelection, renderChildren, - loadChildren + loadChildren, + loadChildrenAPI } = props; const [selectAll, setSelectAll] = useState(false); @@ -131,8 +133,10 @@ const SealTable: React.FC = (props) => { expandable={expandable} rowKey={rowKey} pollingChildren={pollingChildren} + watchChildren={watchChildren} renderChildren={renderChildren} loadChildren={loadChildren} + loadChildrenAPI={loadChildrenAPI} onExpand={onExpand} > ); diff --git a/src/components/seal-table/types.ts b/src/components/seal-table/types.ts index 7534e588..80cea931 100644 --- a/src/components/seal-table/types.ts +++ b/src/components/seal-table/types.ts @@ -30,10 +30,12 @@ export interface SealTableProps { expandable?: React.ReactNode; dataSource: any[]; pollingChildren?: boolean; + watchChildren?: boolean; loading?: boolean; onExpand?: (expanded: boolean, record: any) => void; renderChildren?: (data: any) => React.ReactNode; loadChildren?: (record: any) => Promise; + loadChildrenAPI?: (record: any) => string; rowKey: string; } diff --git a/src/components/util-bar/index.less b/src/components/util-bar/index.less new file mode 100644 index 00000000..11039ce7 --- /dev/null +++ b/src/components/util-bar/index.less @@ -0,0 +1,17 @@ +.util-bar-box { + width: 100%; + height: 100%; + display: flex; + justify-content: space-between; + flex-direction: column; + align-items: center; + + .title { + font-weight: var(--font-weight-medium); + padding: 20px 0; + } + + .ant-progress.ant-progress-circle .ant-progress-text { + font-size: 24px; + } +} diff --git a/src/components/util-bar/index.tsx b/src/components/util-bar/index.tsx new file mode 100644 index 00000000..14f8b287 --- /dev/null +++ b/src/components/util-bar/index.tsx @@ -0,0 +1,52 @@ +import { Progress } from 'antd'; +import './index.less'; + +interface UitilBarProps { + title?: string; + percent: number; + steps?: number; + gapDegree?: number; + strokeWidth?: number; + size?: number; + trailColor?: string; + strokeColor?: string; +} +const UitilBar: React.FC = (props) => { + const { + percent, + steps = 10, + gapDegree = 170, + strokeWidth = 12, + title, + size = 160, + strokeColor, + trailColor = 'rgba(221,221,221,.5)' + } = props; + + const strokeColorFunc = (percent: number) => { + if (percent <= 50) { + return 'var(--ant-color-primary)'; + } + if (percent <= 80) { + return 'var(--ant-color-warning)'; + } + return 'var(--ant-color-error)'; + }; + return ( +
+ {title && {title}} + +
+ ); +}; + +export default UitilBar; diff --git a/src/config/index.ts b/src/config/index.ts index 3a0c7372..90f05993 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -39,9 +39,9 @@ export const StatusMaps = { }; export const WatchEventType = { - CREATE: 'ADDED', - UPDATE: 'MODIFIED', - DELETE: 'DELETED' + CREATE: 1, + UPDATE: 2, + DELETE: 3 }; export const PasswordReg = diff --git a/src/global.less b/src/global.less index ca0c5c94..a3c41e16 100644 --- a/src/global.less +++ b/src/global.less @@ -15,6 +15,7 @@ html { --border-radius-small: 8px; --color-white-1: rgba(255, 255, 255, 100%); --font-weight-normal: 500; + --font-weight-medium: 600; --font-weight-bold: 700; --color-text-1: var(--ant-color-text); --color-bg-light-1: #f0fff6; diff --git a/src/hooks/use-chunk-request.ts b/src/hooks/use-chunk-request.ts index fa1d9cf7..953ecfc9 100644 --- a/src/hooks/use-chunk-request.ts +++ b/src/hooks/use-chunk-request.ts @@ -162,6 +162,7 @@ const useSetChunkRequest = () => { let result = response; let cres = ''; + console.log('chunkrequest============e==', e); if (contentType === 'json') { const currentRes = sliceData(response, e.loaded, loadedSize); result = parseData(currentRes); @@ -183,6 +184,7 @@ const useSetChunkRequest = () => { retryCount.current -= 1; } } catch (error) { + console.log('error=============', error); if (!axios.isCancel(error)) { setRequestReadyState(4); if (retryCount.current > 0) { diff --git a/src/hooks/use-event-source.ts b/src/hooks/use-event-source.ts index 0f3dbe37..9ce9809d 100644 --- a/src/hooks/use-event-source.ts +++ b/src/hooks/use-event-source.ts @@ -1,5 +1,5 @@ import qs from 'query-string'; -import { useRef } from 'react'; +import { useEffect, useRef } from 'react'; export const createEventSourceURL = (url: string) => { const { host, protocol } = window.location; @@ -42,9 +42,14 @@ export default function useEventSource() { onmessage(data); } catch (error) { // error + console.log('event source error: ', error); } }; + eventSourceRef.current.onclose = () => { + console.log('event source closed...'); + }; + eventSourceRef.current.onopen = () => { console.log('event source connected...'); }; @@ -54,6 +59,12 @@ export default function useEventSource() { }; }; + useEffect(() => { + return () => { + eventSourceRef.current?.close?.(); + }; + }, []); + return { eventSourceRef: eventSourceRef, createEventSourceConnection diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index e31f200e..a19ebd32 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -1,5 +1,6 @@ // @ts-nocheck +import { userAtom } from '@/atoms/user'; import { logout } from '@/pages/login/apis'; import { useAccessMarkedRoutes } from '@@/plugin-access'; import { useModel } from '@@/plugin-model'; @@ -15,6 +16,7 @@ import { useNavigate, type IRoute } from '@umijs/max'; +import { useAtom } from 'jotai'; import { useMemo } from 'react'; import Exception from './Exception'; import './Layout.css'; @@ -77,6 +79,7 @@ const mapRoutes = (routes: IRoute[], role: string) => { }; export default (props: any) => { + const [userInfo] = useAtom(userAtom); const location = useLocation(); const navigate = useNavigate(); const intl = useIntl(); @@ -149,9 +152,21 @@ export default (props: any) => { navigate('/'); }} onPageChange={(route) => { - console.log('onRouteChange', initialState, route); const { location } = history; + + // 如果没有修改密码,重定向到修改密码 + console.log('onPageChange', initialState); + if ( + location.pathname !== loginPath && + userInfo?.require_password_change + ) { + history.push(loginPath); + + return; + } + // 如果没有登录,重定向到 login + if (!initialState?.currentUser && location.pathname !== loginPath) { history.push(loginPath); } else if (location.pathname === '/') { diff --git a/src/locales/en-US/users.ts b/src/locales/en-US/users.ts index 4f9959fe..9c801078 100644 --- a/src/locales/en-US/users.ts +++ b/src/locales/en-US/users.ts @@ -18,5 +18,6 @@ export default { 'users.password.lowercase': 'At least one lowercase letter', 'users.password.number': 'At least one number', 'users.password.special': 'At least one special character', - 'users.password.length': 'Length between 6 and 12 characters' + 'users.password.length': 'Length between 6 and 12 characters', + 'users.password.modify.title': 'Modify Password' }; diff --git a/src/locales/zh-CN/users.ts b/src/locales/zh-CN/users.ts index 4ddc62a5..6c2bb54e 100644 --- a/src/locales/zh-CN/users.ts +++ b/src/locales/zh-CN/users.ts @@ -18,5 +18,6 @@ export default { 'users.password.lowercase': '至少包含一个小写字母', 'users.password.number': '至少包含一个数字', 'users.password.special': '至少包含一个特殊字符', - 'users.password.length': '长度在6至12个字符之间' + 'users.password.length': '长度在6至12个字符之间', + 'users.password.modify.title': '修改密码' }; diff --git a/src/pages/dashboard/components/system-load.tsx b/src/pages/dashboard/components/system-load.tsx index 3a2a541e..7e6e1fa2 100644 --- a/src/pages/dashboard/components/system-load.tsx +++ b/src/pages/dashboard/components/system-load.tsx @@ -1,11 +1,11 @@ import CardWrapper from '@/components/card-wrapper'; -import LiquidChart from '@/components/charts/liquid'; import PageTools from '@/components/page-tools'; import breakpoints from '@/config/breakpoints'; import useWindowResize from '@/hooks/use-window-resize'; import { Col, DatePicker, Row } from 'antd'; import _ from 'lodash'; import { useContext, useEffect, useState } from 'react'; +import UitilBar from '../../../components/util-bar'; import { DashboardContext } from '../config/dashboard-context'; import ResourceUtilization from './resource-utilization'; @@ -64,40 +64,28 @@ const SystemLoad = () => { - + percent={_.round(data.gpu?.utilization_rate || 0, 1)} + > - + percent={_.round(data.gpu_memory?.utilization_rate || 0, 1)} + > - + percent={_.round(data.cpu?.utilization_rate || 0, 1)} + > - + percent={_.round(data.memory?.utilization_rate || 0, 1)} + > diff --git a/src/pages/llmodels/config/index.ts b/src/pages/llmodels/config/index.ts index c0c377c0..4dceffef 100644 --- a/src/pages/llmodels/config/index.ts +++ b/src/pages/llmodels/config/index.ts @@ -11,6 +11,12 @@ export const ollamaModelOptions = [ { label: 'deepseek-coder', value: 'deepseek-coder' } ]; +export const modelSourceMap = { + huggingface: 'huggingface', + ollama_library: 'ollama_library', + s3: 's3' +}; + export const status: any = { Running: StatusMaps.success }; diff --git a/src/pages/llmodels/index.tsx b/src/pages/llmodels/index.tsx index 8fced94b..d2cfe2ee 100644 --- a/src/pages/llmodels/index.tsx +++ b/src/pages/llmodels/index.tsx @@ -8,10 +8,12 @@ import type { PageActionType } from '@/config/types'; import useSetChunkRequest, { createAxiosToken } from '@/hooks/use-chunk-request'; +import useEventSource from '@/hooks/use-event-source'; import useTableRowSelection from '@/hooks/use-table-row-selection'; import useTableSort from '@/hooks/use-table-sort'; import useUpdateChunkedList from '@/hooks/use-update-chunk-list'; import { handleBatchRequest } from '@/utils'; +import { fetchChunkedData, readStreamData } from '@/utils/fetch-chunk-data'; import { DeleteOutlined, EditOutlined, @@ -62,6 +64,7 @@ const Models: React.FC = () => { const { sortOrder, setSortOrder } = useTableSort({ defaultSortOrder: 'descend' }); + const { createEventSourceConnection, eventSourceRef } = useEventSource(); const [logContent, setLogContent] = useState(''); const [openLogModal, setOpenLogModal] = useState(false); const [hoverChildIndex, setHoverChildIndex] = useState(-1); @@ -157,12 +160,9 @@ const Models: React.FC = () => { chunkRequedtRef.current?.current?.cancel?.(); try { chunkRequedtRef.current = setChunkRequest({ - url: MODELS_API, + url: `${MODELS_API}`, params: { - ..._.pickBy( - _.omit(queryParams, ['page', 'perPage']), - (val: any) => !!val - ) + ..._.pickBy(queryParams, (val: any) => !!val) }, handler: updateHandler }); @@ -171,6 +171,38 @@ const Models: React.FC = () => { } }; + const createModelsDataByFetch = async () => { + const result = await fetchChunkedData({ + params: { + ..._.pickBy(queryParams, (val: any) => !!val), + watch: true + }, + method: 'GET', + url: `/v1${MODELS_API}` + }); + if (!result) { + return; + } + const { reader, decoder } = result; + + await readStreamData(reader, decoder, (data: any) => { + console.log('streamData=========', data); + }); + }; + + const createModelEvent = () => { + createEventSourceConnection({ + url: `v1${MODELS_API}`, + params: { + ..._.pickBy(queryParams, (val: any) => !!val), + watch: true + }, + onmessage: (data: any) => { + console.log('event source message: ', data); + } + }); + }; + const handleSearch = (e: any) => { fetchData(); }; @@ -327,6 +359,10 @@ const Models: React.FC = () => { return data.items || []; }, []); + const generateChildrenRequestAPI = (params: any) => { + return `${MODELS_API}/${params.id}/instances`; + }; + const handleEdit = (row: ListItem) => { setCurrentData(row); setOpenAddModal(true); @@ -336,9 +372,12 @@ const Models: React.FC = () => { useEffect(() => { fetchData(); + // createModelsDataByFetch(); + createModelEvent(); }, [queryParams]); useEffect(() => { + // watch models list createModelsChunkRequest(); return () => { chunkRequedtRef.current?.current?.cancel?.(); @@ -359,7 +398,7 @@ const Models: React.FC = () => { item.download_progress !== 100 ? 'skeleton-loading' : '' } > - + {item.gpu_index} @@ -385,7 +424,9 @@ const Models: React.FC = () => { )} - {dayjs(item.updated_at).format('YYYY-MM-DD HH:mm:ss')} + + {dayjs(item.updated_at).format('YYYY-MM-DD HH:mm:ss')} + {hoverChildIndex === `${item.id}-${index}` && ( @@ -483,7 +524,9 @@ const Models: React.FC = () => { expandable={true} onChange={handleTableChange} pollingChildren={false} + watchChildren={true} loadChildren={getModelInstances} + loadChildrenAPI={generateChildrenRequestAPI} renderChildren={renderChildren} pagination={{ showSizeChanger: true, @@ -497,17 +540,10 @@ const Models: React.FC = () => { > { - return ( - <> - {text} - - ); - }} /> { - return request(`${AUTH_API}/logout`, { + await request(`${AUTH_API}/logout`, { method: 'POST' }); + clearAtomStorage(userAtom); + return; }; export const accessToken = async () => { diff --git a/src/pages/login/components/login-form.tsx b/src/pages/login/components/login-form.tsx index a5f2e35f..d9fbea9d 100644 --- a/src/pages/login/components/login-form.tsx +++ b/src/pages/login/components/login-form.tsx @@ -1,9 +1,10 @@ import LogoIcon from '@/assets/images/logo.png'; +import { userAtom } from '@/atoms/user'; import SealInput from '@/components/seal-form/seal-input'; import { GlobalOutlined, LockOutlined, UserOutlined } from '@ant-design/icons'; import { SelectLang, history, useIntl, useModel } from '@umijs/max'; import { Button, Checkbox, Form } from 'antd'; -import { useEffect } from 'react'; +import { useAtom } from 'jotai'; import { flushSync } from 'react-dom'; import { login } from '../apis'; @@ -23,22 +24,16 @@ const renderLogo = () => { ); }; -const LoginForm: React.FC<{ - setCurrentUser: (userInfo: any) => void; -}> = ({ setCurrentUser }) => { +const LoginForm = () => { + const [userInfo, setUserInfo] = useAtom(userAtom); const { initialState, setInitialState } = useModel('@@initialState'); const { globalState, setGlobalState } = useModel('global'); const intl = useIntl(); const [form] = Form.useForm(); - useEffect(() => { - console.log('initstate===', { - initialState, - globalState - }); - }, []); const gotoDefaultPage = (userInfo: any) => { - const pathname = userInfo?.is_admin ? '/dashboard' : '/playground'; + const pathname = + userInfo && userInfo?.is_admin ? '/dashboard' : '/playground'; history.push(pathname); }; const fetchUserInfo = async () => { @@ -66,13 +61,11 @@ const LoginForm: React.FC<{ setGlobalState({ userInfo }); - // if (userInfo?.require_password_change) { - // setCurrentUser(userInfo); - // } else { - // setCurrentUser(null); - // gotoDefaultPage(userInfo); - // } - gotoDefaultPage(userInfo); + setUserInfo(userInfo); + if (!userInfo?.require_password_change) { + gotoDefaultPage(userInfo); + } + // gotoDefaultPage(userInfo); } catch (error) { console.log('error====', error); } diff --git a/src/pages/login/components/password-form.tsx b/src/pages/login/components/password-form.tsx index e6651c47..f1d325e2 100644 --- a/src/pages/login/components/password-form.tsx +++ b/src/pages/login/components/password-form.tsx @@ -1,23 +1,20 @@ +import { 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, useModel } from '@umijs/max'; +import { SelectLang, history, useIntl } from '@umijs/max'; import { Button, Form, message } from 'antd'; -import { useEffect } from 'react'; +import { useAtom } from 'jotai'; import { updatePassword } from '../apis'; const PasswordForm: React.FC = () => { - const { globalState, setGlobalState } = useModel('global'); - const [currentUser, setCurrentUser] = useState(null); const intl = useIntl(); const [form] = Form.useForm(); - useEffect(() => { - console.log('initstate===', { - globalState - }); - }, []); + const [userInfo, setUserInfo] = useAtom(userAtom); const gotoDefaultPage = (userInfo: any) => { - const pathname = userInfo?.is_admin ? '/dashboard' : '/playground'; + const pathname = + userInfo && userInfo?.is_admin ? '/dashboard' : '/playground'; history.push(pathname); }; @@ -26,9 +23,14 @@ const PasswordForm: React.FC = () => { try { await updatePassword({ new_password: values.new_password, - comfirm_password: values.comfirm_password + current_password: values.current_password }); - gotoDefaultPage(currentUser); + + await setUserInfo({ + ...userInfo, + require_password_change: false + }); + gotoDefaultPage(userInfo); message.success(intl.formatMessage({ id: 'common.message.success' })); } catch (error) { console.log('error====', error); @@ -45,40 +47,45 @@ const PasswordForm: React.FC = () => { style={{ width: '400px', margin: '0 auto', paddingTop: '5%' }} onFinish={handleSubmit} > -
修改密码
+

+ {intl.formatMessage({ id: 'users.password.modify.title' })} +

+ + } + label={intl.formatMessage({ id: 'users.form.currentpassword' })} + /> + } - label={intl.formatMessage({ id: 'common.form.password' })} - /> - - - } - label={intl.formatMessage({ id: 'common.form.password' })} + label={intl.formatMessage({ id: 'users.form.newpassword' })} /> diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 10c54667..78fd237b 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -1,13 +1,28 @@ -import { useState } from 'react'; +import { userAtom } from '@/atoms/user'; +import { history } from '@umijs/max'; +import { useAtom } from 'jotai'; +import { useEffect } from 'react'; import LoginForm from './components/login-form'; +import PasswordForm from './components/password-form'; const Login = () => { - const [currentUser, setCurrentUser] = useState(null); + const [userInfo, setUserInfo] = useAtom(userAtom); + + const gotoDefaultPage = (userInfo: any) => { + if (!userInfo || userInfo?.require_password_change) { + return; + } + const pathname = userInfo?.is_admin ? '/dashboard' : '/playground'; + + history.push(pathname, { replace: true }); + }; + useEffect(() => { + gotoDefaultPage(userInfo); + }, [userInfo]); return (
- - {/* */} + {userInfo?.require_password_change ? : }
); }; diff --git a/src/request-config.ts b/src/request-config.ts index 9e8696e8..540ce260 100644 --- a/src/request-config.ts +++ b/src/request-config.ts @@ -1,3 +1,5 @@ +import { userAtom } from '@/atoms/user'; +import { clearAtomStorage } from '@/atoms/utils'; import { RequestConfig, history } from '@umijs/max'; import { message } from 'antd'; @@ -9,14 +11,17 @@ export const requestConfig: RequestConfig = { // to do something }, errorHandler: (error: any, opts: any) => { - if (opts?.skipErrorHandler) throw error; const { message: errorMessage, response } = error; const errMsg = response?.data?.message || errorMessage; - message.error(errMsg); - if (response.status === 401) { - history.push('/login', { replace: true }); + if (!opts?.skipErrorHandler) { + message.error(errMsg); } console.log('errorHandler+++++++++++++++', error, opts); + if (response.status === 401) { + clearAtomStorage(userAtom); + + history.push('/login', { replace: true }); + } } }, requestInterceptors: [