dark
7 months ago
19 changed files with 319 additions and 77 deletions
-
2mock/menus.ts
-
2package.json
-
7src/App.tsx
-
11src/layout/EmptyLayout.tsx
-
15src/pages/login/index.tsx
-
5src/pages/system/departments/index.tsx
-
6src/pages/system/menus/index.tsx
-
5src/pages/system/roles/index.tsx
-
5src/pages/system/users/index.tsx
-
65src/request.ts
-
128src/routes.tsx
-
2src/service/base.ts
-
3src/service/system.ts
-
57src/store/system.ts
-
25src/types.d.ts
-
18src/types/menus.d.ts
-
26src/utils/index.ts
-
6tsconfig.json
-
8vite.config.ts
@ -1,10 +1,15 @@ |
|||
import './App.css' |
|||
import { RootProvider } from './routes.tsx' |
|||
import { Provider } from 'jotai' |
|||
import { appStore } from '@/store/system.ts' |
|||
|
|||
function App() { |
|||
|
|||
|
|||
return ( |
|||
<RootProvider/> |
|||
<Provider store={appStore}> |
|||
<RootProvider/> |
|||
</Provider> |
|||
) |
|||
} |
|||
|
|||
|
@ -0,0 +1,11 @@ |
|||
import { Outlet } from '@tanstack/react-router' |
|||
|
|||
const EmptyLayout = () => { |
|||
return ( |
|||
<> |
|||
<Outlet/> |
|||
</> |
|||
) |
|||
} |
|||
|
|||
export default EmptyLayout |
@ -0,0 +1,15 @@ |
|||
import { createFileRoute } from '@tanstack/react-router' |
|||
|
|||
const Login = () => { |
|||
return ( |
|||
<div> |
|||
Login |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export const Route = createFileRoute("/login")({ |
|||
component: Login |
|||
}) |
|||
|
|||
export default Login |
@ -1,19 +1,76 @@ |
|||
import axios, { AxiosRequestConfig } from 'axios' |
|||
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios' |
|||
import { message } from 'antd' |
|||
import { getToken, setToken } from '@/store/system.ts' |
|||
|
|||
|
|||
export type { AxiosRequestConfig } |
|||
|
|||
|
|||
export const request = axios.create({ |
|||
baseURL: '/api/v1', |
|||
timeout: 1000, |
|||
// timeout: 1000,
|
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
}) |
|||
|
|||
//拦截request,添加token
|
|||
request.interceptors.request.use((config) => { |
|||
|
|||
const token = getToken() |
|||
if (token) { |
|||
config.headers.Authorization = `Bearer ${token}` |
|||
} |
|||
|
|||
if (window.location.pathname === '/login') { |
|||
|
|||
throw new Error('login') |
|||
|
|||
} else { |
|||
const search = new URLSearchParams(window.location.search) |
|||
let url = `/login?redirect=${encodeURIComponent(window.location.pathname)}` |
|||
if (search.toString() !== '') { |
|||
url = `/login?redirect=${encodeURIComponent(window.location.pathname + '?=' + search.toString())}` |
|||
} |
|||
window.location.href = url |
|||
} |
|||
return config |
|||
}) |
|||
|
|||
|
|||
//拦截response,返回data
|
|||
request.interceptors.response.use((response) => { |
|||
request.interceptors.response.use((response: AxiosResponse) => { |
|||
// console.log('response', response.data)
|
|||
return response.data |
|||
|
|||
message.destroy() |
|||
|
|||
switch (response.data.code) { |
|||
case 200: |
|||
//login
|
|||
if (response.config.url?.includes('/sys/login')) { |
|||
setToken(response.data.data.token) |
|||
} |
|||
return response.data |
|||
case 401: |
|||
// 401: 未登录
|
|||
message.error('登录失败,跳转重新登录') |
|||
// eslint-disable-next-line no-case-declarations
|
|||
const search = new URLSearchParams(window.location.search) |
|||
// eslint-disable-next-line no-case-declarations
|
|||
let redirect = window.location.pathname |
|||
if (search.toString() !== '') { |
|||
redirect = window.location.pathname + '?=' + search.toString() |
|||
} |
|||
window.location.href = `/login?redirect=${encodeURIComponent(redirect)}` |
|||
break |
|||
default: |
|||
message.error(response.data.message) |
|||
return Promise.reject(response) |
|||
} |
|||
|
|||
}, (error) => { |
|||
console.log('error', error) |
|||
return Promise.reject(error) |
|||
}) |
|||
|
|||
export default request |
@ -0,0 +1,26 @@ |
|||
import { IMenu } from '@/types/menus' |
|||
import { MenuItem } from '@/types' |
|||
import { getIcon } from '@/components/icon' |
|||
|
|||
|
|||
// 格式化菜单数据, 把children转换成routes
|
|||
export const formatMenuData = (data: IMenu[]) => { |
|||
const result: MenuItem[] = [] |
|||
for (const item of data) { |
|||
if (item.icon && typeof item.icon === 'string') { |
|||
item.icon = getIcon(item.icon as string, { size: '14', theme: 'filled' }) |
|||
} |
|||
if (!item.children || !item.children.length) { |
|||
result.push(item) |
|||
} else { |
|||
const { children, ...other } = item |
|||
result.push({ |
|||
...other, |
|||
children, |
|||
routes: formatMenuData(children), |
|||
}) |
|||
|
|||
} |
|||
} |
|||
return result |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue