From 7be3de5101fdcce3f8e153c5cdb459d00993d598 Mon Sep 17 00:00:00 2001 From: dark Date: Fri, 19 Apr 2024 01:29:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=99=BB=E5=BD=95=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 6 ++- src/assets/login.png | Bin 0 -> 5349 bytes src/layout/RootLayout.tsx | 4 +- src/locales/lang/en-US.ts | 11 +++++ src/locales/lang/zh-CN.ts | 11 +++++ src/pages/login/index.css | 123 ++++++++++++++++++++++++++++++++++++++++++++++ src/pages/login/index.tsx | 100 +++++++++++++++++++++++++++++++++---- src/service/base.ts | 2 +- src/service/system.ts | 7 +++ src/store/system.ts | 20 +++++--- src/store/user.ts | 24 ++++++++- src/types/login.d.ts | 1 - src/types/menus.d.ts | 1 + src/utils/index.ts | 14 ++++-- 14 files changed, 297 insertions(+), 27 deletions(-) create mode 100644 src/assets/login.png create mode 100644 src/pages/login/index.css 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 0000000000000000000000000000000000000000..bafe7aef3b5d3106f63e69da96f57798e1ba1bbb GIT binary patch literal 5349 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 literal 0 HcmV?d00001 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), })