diff --git a/mock/menus.ts b/mock/menus.ts
index 83a8e00..433977e 100644
--- a/mock/menus.ts
+++ b/mock/menus.ts
@@ -2,7 +2,7 @@ import { MockMethod } from 'vite-plugin-mock'
export default [
{
- url: '/api/menus',
+ url: '/api/v1/menus',
method: 'get',
response: () => {
return {
diff --git a/package.json b/package.json
index b817871..1ec5c9a 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
- "dev": "vite --host --port 3000",
+ "dev": "vite --host --port 3000 --debug",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
diff --git a/src/App.tsx b/src/App.tsx
index 5bfce9b..a97602c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -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 (
-
+
+
+
)
}
diff --git a/src/layout/EmptyLayout.tsx b/src/layout/EmptyLayout.tsx
new file mode 100644
index 0000000..bac73dc
--- /dev/null
+++ b/src/layout/EmptyLayout.tsx
@@ -0,0 +1,11 @@
+import { Outlet } from '@tanstack/react-router'
+
+const EmptyLayout = () => {
+ return (
+ <>
+
+ >
+ )
+}
+
+export default EmptyLayout
\ No newline at end of file
diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx
new file mode 100644
index 0000000..fafbe87
--- /dev/null
+++ b/src/pages/login/index.tsx
@@ -0,0 +1,15 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+const Login = () => {
+ return (
+
+ Login
+
+ )
+}
+
+export const Route = createFileRoute("/login")({
+ component: Login
+})
+
+export default Login
\ No newline at end of file
diff --git a/src/pages/system/departments/index.tsx b/src/pages/system/departments/index.tsx
index 097a8b2..a5b0031 100644
--- a/src/pages/system/departments/index.tsx
+++ b/src/pages/system/departments/index.tsx
@@ -1,4 +1,5 @@
import { PageContainer } from '@ant-design/pro-components'
+import { createLazyFileRoute } from '@tanstack/react-router'
const Departments = () => {
return (
@@ -8,4 +9,8 @@ const Departments = () => {
)
}
+export const Route = createLazyFileRoute("/system/departments")({
+ component: Departments
+})
+
export default Departments
\ No newline at end of file
diff --git a/src/pages/system/menus/index.tsx b/src/pages/system/menus/index.tsx
index b1ab2c8..accd27c 100644
--- a/src/pages/system/menus/index.tsx
+++ b/src/pages/system/menus/index.tsx
@@ -4,6 +4,7 @@ import { useAtom, useAtomValue } from 'jotai'
import { menuDataAtom, selectedMenuAtom, selectedMenuIdAtom } from '@/store/system.ts'
import { formatterMenuData } from '@/utils/uuid.ts'
import { CloseOutlined, PlusOutlined } from '@ant-design/icons'
+import { createLazyFileRoute } from '@tanstack/react-router'
const Menus = () => {
@@ -57,4 +58,9 @@ const Menus = () => {
)
}
+export const Route = createLazyFileRoute("/system/menus")({
+ component: Menus
+})
+
+
export default Menus
\ No newline at end of file
diff --git a/src/pages/system/roles/index.tsx b/src/pages/system/roles/index.tsx
index 614f14b..ee54971 100644
--- a/src/pages/system/roles/index.tsx
+++ b/src/pages/system/roles/index.tsx
@@ -1,4 +1,5 @@
import { PageContainer } from '@ant-design/pro-components'
+import { createLazyFileRoute } from '@tanstack/react-router'
const Roles = () => {
return (
@@ -8,4 +9,8 @@ const Roles = () => {
)
}
+export const Route = createLazyFileRoute("/system/roles")({
+ component: Roles
+})
+
export default Roles
\ No newline at end of file
diff --git a/src/pages/system/users/index.tsx b/src/pages/system/users/index.tsx
index 595ef09..baa64e4 100644
--- a/src/pages/system/users/index.tsx
+++ b/src/pages/system/users/index.tsx
@@ -1,4 +1,5 @@
import { PageContainer } from '@ant-design/pro-components'
+import { createLazyFileRoute } from '@tanstack/react-router'
const Users = () => {
return (
@@ -8,4 +9,8 @@ const Users = () => {
)
}
+export const Route = createLazyFileRoute("/system/users")({
+ component: Users
+})
+
export default Users
\ No newline at end of file
diff --git a/src/request.ts b/src/request.ts
index d7486c2..fdf661c 100644
--- a/src/request.ts
+++ b/src/request.ts
@@ -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
\ No newline at end of file
diff --git a/src/routes.tsx b/src/routes.tsx
index cc0d75c..c9cf98f 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -1,16 +1,20 @@
import {
createRouter,
createRoute,
- RouterProvider, AnyRoute, redirect, createRootRouteWithContext, createLazyRoute,
+ RouterProvider, AnyRoute, redirect, createRootRouteWithContext, createLazyRoute, Outlet,
} from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools'
import RootLayout from './layout/RootLayout'
import ListPageLayout from '@/layout/ListPageLayout.tsx'
+import { Route as LoginRouteImport } from '@/pages/login'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import { IRootContext, MenuItem } from './types'
-import { menuDataAtom } from './store/system.ts'
+import { appAtom, menuDataAtom } from './store/system.ts'
+import { useAtom } from 'jotai/index'
+import EmptyLayout from '@/layout/EmptyLayout.tsx'
+
export const queryClient = new QueryClient({
defaultOptions: {
@@ -23,12 +27,10 @@ export const queryClient = new QueryClient({
const rootRoute = createRootRouteWithContext()({
component: () => (
- <>
-
+
+
- >
-
-
+
),
beforeLoad: ({ location }) => {
if (location.pathname === '/') {
@@ -38,13 +40,97 @@ const rootRoute = createRootRouteWithContext()({
notFoundComponent: () => 404 Not Found
,
})
-const generateDynamicRoutes = (menuData: MenuItem[]) => {
+const emptyRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ id: '/empty',
+ component: EmptyLayout,
+})
+
+const layoutRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ id: '/layout',
+ component: RootLayout,
+})
+
+const loginRoute = LoginRouteImport.update({
+ path: '/login',
+ getParentRoute: () => emptyRoute,
+} as any)
+
+const menusRoute = createRoute({
+ getParentRoute: () => layoutRoute,
+ path: '/system/menus',
+}).lazy(async () => await import('@/pages/system/menus').then(d => d.Route))
+
+const departmentsRoute = createRoute({
+ getParentRoute: () => layoutRoute,
+ path: '/system/departments',
+}).lazy(async () => await import('@/pages/system/departments').then(d => d.Route))
+
+const usersRoute = createRoute({
+ getParentRoute: () => layoutRoute,
+ path: '/system/users',
+}).lazy(async () => await import('@/pages/system/users').then(d => d.Route))
+
+const rolesRoute = createRoute({
+ getParentRoute: () => layoutRoute,
+ path: '/system/roles',
+}).lazy(async () => await import('@/pages/system/roles').then(d => d.Route))
+
+
+declare module '@tanstack/react-router' {
+ interface FileRoutesByPath {
+ '/login': {
+ preLoaderRoute: typeof LoginRouteImport
+ parentRoute: typeof rootRoute
+ },
+ '/system/menus': {
+ preLoaderRoute: typeof menusRoute
+ parentRoute: typeof layoutRoute
+ },
+ '/system/departments': {
+ preLoaderRoute: typeof departmentsRoute
+ parentRoute: typeof layoutRoute
+ },
+ '/system/users': {
+ preLoaderRoute: typeof usersRoute
+ parentRoute: typeof layoutRoute
+ },
+ '/system/roles': {
+ preLoaderRoute: typeof rolesRoute
+ parentRoute: typeof layoutRoute
+ },
+ '/welcome': {
+ preLoaderRoute: typeof rootRoute
+ parentRoute: typeof layoutRoute
+ },
+ }
+}
+
+
+const routeTree = rootRoute.addChildren(
+ [
+ loginRoute,
+ emptyRoute,
+ layoutRoute.addChildren(
+ [
+ menusRoute,
+ departmentsRoute,
+ usersRoute,
+ rolesRoute,
+ ]
+ ),
+ ]
+)
+
+
+export const generateDynamicRoutes = (menuData: MenuItem[]) => {
// 递归生成路由,如果有routes则递归生成子路由
const generateRoutes = (menu: MenuItem, parentRoute: AnyRoute) => {
const path = menu.path?.replace(parentRoute.options?.path, '')
- const isLayout = menu.children && menu.children.length > 0 && menu.type === 1
+ const isLayout = menu.children && menu.children.length > 0 && menu.type === 'menu'
if (isLayout && !menu.component) {
//没有component的layout,直接返回
@@ -76,7 +162,7 @@ const generateDynamicRoutes = (menuData: MenuItem[]) => {
// menu.type
// 1,组件(页面),2,IFrame,3,外链接,4,按钮
- if (menu.type === 2) {
+ if (menu.type === 'iframe') {
component = '@/components/Iframe'
}
@@ -85,7 +171,7 @@ const generateDynamicRoutes = (menuData: MenuItem[]) => {
component: () => 404 Not Found
})
}
-
+ /* @vite-ignore */
const d = await import(`${component}`)
if (d.Route) {
return d.Route
@@ -103,7 +189,7 @@ const generateDynamicRoutes = (menuData: MenuItem[]) => {
// 对menuData递归生成路由,只处理type =1 的菜单
const did = (menus: MenuItem[], parentRoute: AnyRoute) => {
- return menus.filter((item) => item.type === 1).map((item) => {
+ return menus.filter((item) => item.type === 'menu').map((item) => {
// 如果有children则递归生成子路由,同样只处理type =1 的菜单
const route = generateRoutes(item, parentRoute)
if (item.children && item.children.length > 0) {
@@ -119,9 +205,15 @@ const generateDynamicRoutes = (menuData: MenuItem[]) => {
return did(menuData, rootRoute)
}
+const router = createRouter({
+ routeTree,
+ context: { queryClient, menuData: undefined },
+ defaultPreload: 'intent'
+})
export const RootProvider = () => {
+ const [ , ] = useAtom(appAtom)
const { data, isError, isPending } = useAtomValue(menuDataAtom)
if (isError) {
@@ -132,13 +224,11 @@ export const RootProvider = () => {
return Loading...
}
- const dynamicRoutes = generateDynamicRoutes(data!)
- const routeTree = rootRoute.addChildren(dynamicRoutes)
-
- const router = createRouter({
- routeTree,
- context: { queryClient, menuData: data },
- defaultPreload: 'intent'
+ router.update({
+ context: {
+ queryClient,
+ menuData: data,
+ }
})
return (
diff --git a/src/service/base.ts b/src/service/base.ts
index af3224f..953c469 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 })
+ return request.get(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 ac45752..04f469f 100644
--- a/src/service/system.ts
+++ b/src/service/system.ts
@@ -1,13 +1,14 @@
import request from '../request.ts'
import { LoginRequest, LoginResponse } from '@/types/login'
import { createCURD } from '@/service/base.ts'
+import { IMenu } from '@/types/menus'
const systemServ = {
dept: {
...createCURD('/sys/dept')
},
menus: {
- ...createCURD('/sys/menu')
+ ...createCURD('/sys/menu')
},
login: (data: LoginRequest) => {
return request.post('/sys/login', data)
diff --git a/src/store/system.ts b/src/store/system.ts
index 41adbb6..f098361 100644
--- a/src/store/system.ts
+++ b/src/store/system.ts
@@ -1,37 +1,42 @@
import { atomWithQuery } from 'jotai-tanstack-query'
import systemServ from '../service/system.ts'
-import { MenuItem } from '@/types'
-import { getIcon } from '@/components/icon'
-import { atom } from 'jotai/index'
-
-// 格式化菜单数据, 把children转换成routes
-export const formatMenuData = (data: MenuItem[]) => {
- 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
+import { IAppData, MenuItem } from '@/types'
+import { atom, createStore } from 'jotai'
+import { atomWithStorage } from 'jotai/utils'
+import { formatMenuData } from '@/utils'
+
+
+/**
+ * app全局状态
+ */
+
+export const appStore = createStore()
+export const appAtom = atomWithStorage>('app', {
+ name: 'Crazy Pro',
+ version: '1.0.0',
+ language: 'zh-CN',
+})
+
+
+export const getAppData = () => {
+ return appStore.get(appAtom)
+}
+
+export const getToken = () => {
+ return appStore.get(appAtom).token
+}
+
+export const setToken = (token: string) => {
+ appStore.set(appAtom, { token })
}
export const menuDataAtom = atomWithQuery(() => ({
queryKey: [ 'menus' ],
queryFn: async () => {
+ if (window.location.pathname === '/login') return []
return await systemServ.menus.list()
},
- select: data => formatMenuData(data.data ?? []),
+ select: data => formatMenuData(data as any ?? []),
}))
export const selectedMenuIdAtom = atom(0)
@@ -39,8 +44,8 @@ export const selectedMenuAtom = atom