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')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/**/}
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+})
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),
})