diff --git a/src/App.tsx b/src/App.tsx index 07a49c4..24bb8ff 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,23 +7,25 @@ import { Provider, useAtom, useAtomValue } from 'jotai' import './App.css' import { useEffect } from 'react' import { RootProvider } from './routes.tsx' +import PageLoading from '@/components/page-loading' function App() { const [ appData, ] = useAtom(appAtom) - const { data, isError, isPending } = useAtomValue(menuDataAtom) + const { data, isError, error, isPending } = useAtomValue(menuDataAtom) useEffect(() => { initI18n() }, []) if (isError) { + console.error(error) return
Error
} if (isPending) { - return
Loading...
+ return } return ( diff --git a/src/assets/login.png b/src/assets/login.png new file mode 100644 index 0000000..bafe7ae Binary files /dev/null and b/src/assets/login.png differ diff --git a/src/layout/RootLayout.tsx b/src/layout/RootLayout.tsx index 01bef06..6d88870 100644 --- a/src/layout/RootLayout.tsx +++ b/src/layout/RootLayout.tsx @@ -122,10 +122,10 @@ export default () => { )} menuItemRender={(item, dom) => { return
{ - setPathname(item.path || '/welcome') + setPathname(item.path || '/dashboard') }} > - + {dom}
diff --git a/src/locales/lang/en-US.ts b/src/locales/lang/en-US.ts index d31a2f1..c964dce 100644 --- a/src/locales/lang/en-US.ts +++ b/src/locales/lang/en-US.ts @@ -24,6 +24,17 @@ export default { logout: 'logout', } }, + login:{ + title: 'Account Password Login', + username: 'Username', + usernameMsg: 'Please enter your username', + password: 'Password', + passwordMsg: 'Please enter your password', + code: 'Verification Code', + codeMsg: 'Please enter the verification code', + submit: 'Login', + success: 'Login success' + }, home: { welcome: 'Welcome to' }, diff --git a/src/locales/lang/zh-CN.ts b/src/locales/lang/zh-CN.ts index 9f22189..d38f419 100644 --- a/src/locales/lang/zh-CN.ts +++ b/src/locales/lang/zh-CN.ts @@ -23,6 +23,17 @@ export default { logout: '退出登录', } }, + login:{ + title: '账户密码登录', + username: '用户名', + usernameMsg: '请输入用户名', + password: '密码', + passwordMsg: '请输入密码', + code: '验证码', + codeMsg: '请输入验证码', + submit: '登录', + success: '登录成功' + }, home: { welcome: '欢迎使用' }, diff --git a/src/pages/login/index.css b/src/pages/login/index.css new file mode 100644 index 0000000..9c7c405 --- /dev/null +++ b/src/pages/login/index.css @@ -0,0 +1,123 @@ +body { + margin: 0; + padding: 0; + font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; + + overflow: hidden; + + .login-container { + display: flex; + align-items: center; + height: 100vh; + background-image: url("@/assets/login.png"); + background-repeat: no-repeat; + background-size: cover; + + + .language { + position: absolute; + top: 10px; + right: 10px; + /*color: #fff;*/ + font-size: 14px; + cursor: pointer; + + } + + .loginBlock { + width: 100%; + height: 100%; + padding: 40px 0; + display: flex; + align-items: center; + justify-content: center; + } + + .innerBlock { + width: 356px; + margin: 0 auto; + } + + .logo { + height: 30px; + } + + .infoLine { + display: flex; + align-items: center; + justify-content: space-between; + margin: 0; + } + + .infoLeft { + color: #666; + font-size: 14px; + } + + .desc { + margin: 24px 0; + color: #999; + font-size: 16px; + cursor: pointer; + } + + .active { + color: #333; + font-weight: bold; + font-size: 24px; + } + + .line { + display: inline-block; + width: 1px; + height: 12px; + background: #999; + } + + .innerBeforeInput { + margin-left: 10px; + color: #999; + } + + .innerBeforeInput .line { + margin-left: 10px; + } + + .innerAfterInput { + margin-right: 10px; + color: #999; + } + + .innerAfterInput .line { + margin-right: 10px; + vertical-align: middle; + } + + .sendCode { + max-width: 65px; + margin-right: 10px; + } + + .otherLogin { + color: #666; + font-size: 14px; + } + + .icon { + margin-left: 10px; + } + + .link { + color: #5584ff; + font-size: 14px; + text-align: left; + } + + .submitBtn { + width: 100%; + } + } + + +} + diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 1df32ec..3970c62 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -1,17 +1,99 @@ import SelectLang from '@/components/select-lang' -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute, useSearch, useNavigate } from '@tanstack/react-router' +import { Button, Form, Input, message, Space } from 'antd' +import { useAtom } from 'jotai' +import './index.css' +import { useTranslation } from '@/i18n.ts' +import { loginAtom, loginFormAtom } from '@/store/user.ts' +import { memo, useEffect } from 'react' -const Login = () => { - return ( -
- -
- ) -} +const Login = memo(() => { + + const navigator = useNavigate() + // @ts-ignore 从url中获取redirect参数 + const search = useSearch({ form: '/login' }) + const { t } = useTranslation() + const [ values, setValues ] = useAtom(loginFormAtom) + const [ { isPending, isSuccess, mutate } ] = useAtom(loginAtom) + const [ form ] = Form.useForm() + + const handleSubmit = () => { + form.validateFields().then(() => { + mutate(values) + }) + } + + useEffect(() => { + if (isSuccess) { + message.success(t('login.success')) + navigator({ + to: search?.redirect ?? '/' + }) + } + }, [ isSuccess ]) + + return ( +
+
+ +
+
+
+ +
+ + {t('login.title')} + + +
+ +
{ + setValues(allValues) + }} + size="large"> + + + + + + + + + + + {/*验证码*/} + + + + + + + +
+
+
+ +
+ ) +}) export const Route = createFileRoute('/login')({ - component: Login + component: Login }) export default Login \ No newline at end of file diff --git a/src/service/base.ts b/src/service/base.ts index 953c469..a7d271f 100644 --- a/src/service/base.ts +++ b/src/service/base.ts @@ -9,7 +9,7 @@ export const createCURD = (api: string, options?: AxiosRequest return { list: (params?: TParams & TPage) => { - return request.get(api, { ...options, params }).then(data=>data.data) + return request.post(api, { ...options, params }).then(data=>data.data) }, add: (data: TParams) => { return request.post(`${api}/add`, data, options) diff --git a/src/service/system.ts b/src/service/system.ts index 04f469f..cce8e89 100644 --- a/src/service/system.ts +++ b/src/service/system.ts @@ -12,8 +12,15 @@ const systemServ = { }, login: (data: LoginRequest) => { return request.post('/sys/login', data) + }, + user:{ + menus:()=>{ + return request.get('/sys/user/menus') + } + } + } export default systemServ \ No newline at end of file diff --git a/src/store/system.ts b/src/store/system.ts index 1eb1ff2..68e5131 100644 --- a/src/store/system.ts +++ b/src/store/system.ts @@ -1,11 +1,11 @@ import { IAppData, MenuItem } from '@/types' import { formatMenuData } from '@/utils' -import { isAuthenticated } from '@/utils/auth.ts' import { atom, createStore } from 'jotai' import { atomWithQuery } from 'jotai-tanstack-query' import { atomWithStorage } from 'jotai/utils' import systemServ from '../service/system.ts' import { changeLanguage as setLang } from 'i18next' +import { AxiosResponse } from 'axios' /** * app全局状态 @@ -18,6 +18,10 @@ export const appAtom = atomWithStorage>('app', { language: 'zh-CN', }) +appStore.sub(appAtom, () => { + +}) + export const getAppData = () => { return appStore.get(appAtom) @@ -50,15 +54,15 @@ export const setToken = (token: string) => { updateAppData({ token }) } -export const menuDataAtom = atomWithQuery(() => ({ - queryKey: [ 'menus' ], +export const menuDataAtom = atomWithQuery((get) => ({ + enabled: !!get(appAtom).token, + queryKey: [ 'user_menus', get(appAtom).token ], queryFn: async () => { - if (!isAuthenticated()) { - return [] - } - return await systemServ.menus.list() + return await systemServ.user.menus() }, - select: data => formatMenuData(data as any ?? []), + select: (data: AxiosResponse) => { + return formatMenuData(data.data.rows as any ?? []) + } })) export const selectedMenuIdAtom = atom(0) diff --git a/src/store/user.ts b/src/store/user.ts index 036e0d4..68f6c3d 100644 --- a/src/store/user.ts +++ b/src/store/user.ts @@ -1,8 +1,30 @@ import { atom } from 'jotai/index' -import { IAuth } from '../types' +import { IAuth } from '@/types' +import { LoginRequest } from '@/types/login' +import { atomWithMutation } from 'jotai-tanstack-query' +import systemServ from '@/service/system.ts' +import { isDev } from '@/utils' export const authAtom = atom({ isLogin: false, authKey: [] }) +const devLogin = { + username: 'SupperAdmin', + password: 'kk123456', + code: '123456' +} +export const loginFormAtom = atom({ + ...(isDev ? devLogin : {}) +} as LoginRequest) + +export const loginAtom = atomWithMutation(() => ({ + mutationKey: [ 'login' ], + mutationFn: async (params) => { + return await systemServ.login(params) + }, + onSuccess: (data) => { + console.log('login success', data) + } +})) diff --git a/src/types/login.d.ts b/src/types/login.d.ts index 739ab2f..b22a93e 100644 --- a/src/types/login.d.ts +++ b/src/types/login.d.ts @@ -1,4 +1,3 @@ - export interface LoginLogRequest { key: string, start: string, diff --git a/src/types/menus.d.ts b/src/types/menus.d.ts index f995485..c87e0b7 100644 --- a/src/types/menus.d.ts +++ b/src/types/menus.d.ts @@ -8,6 +8,7 @@ export interface MenuButton { export interface IMenu { id: number, + key: string, parent_id: number, sort: number, code: string, diff --git a/src/utils/index.ts b/src/utils/index.ts index 9520b08..7179adb 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -2,6 +2,8 @@ import { IMenu } from '@/types/menus' import { MenuItem } from '@/types' import { getIcon } from '@/components/icon' +//vite环境变量, 判断是否是开发环境 +export const isDev = import.meta.env.MODE === 'development' // 格式化菜单数据, 把children转换成routes export const formatMenuData = (data: IMenu[]) => { @@ -11,12 +13,18 @@ export const formatMenuData = (data: IMenu[]) => { item.icon = getIcon(item.icon as string, { size: '14', theme: 'filled' }) } if (!item.children || !item.children.length) { - result.push(item) + result.push({ + ...item, + key: item.name, + name: item.title + }) } else { - const { children, ...other } = item + const { children, name, ...other } = item result.push({ ...other, - children, + key: name, + name: other.title, + children: formatMenuData(children), routes: formatMenuData(children), })