diff --git a/package.json b/package.json
index 1ec5c9a..8b71454 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
- "dev": "vite --host --port 3000 --debug",
+ "dev": "vite --host --port 3000",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
@@ -22,10 +22,13 @@
"antd": "^5.16.1",
"axios": "^1.6.8",
"dayjs": "^1.11.10",
+ "i18next": "^23.11.2",
+ "i18next-browser-languagedetector": "^7.2.1",
"jotai": "^2.8.0",
"jotai-tanstack-query": "^0.8.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-i18next": "^14.1.0",
"wonka": "^6.3.4"
},
"devDependencies": {
diff --git a/src/App.tsx b/src/App.tsx
index a97602c..dd963c7 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,16 +1,26 @@
+import { appAtom, appStore, menuDataAtom } from '@/store/system.ts'
+import { Provider, useAtom, useAtomValue } from 'jotai'
import './App.css'
import { RootProvider } from './routes.tsx'
-import { Provider } from 'jotai'
-import { appStore } from '@/store/system.ts'
function App() {
+ const [ , ] = useAtom(appAtom)
+ const { data, isError, isPending } = useAtomValue(menuDataAtom)
- return (
-
-
-
- )
+ if (isError) {
+ return
Error
+ }
+
+ if (isPending) {
+ return Loading...
+ }
+
+ return (
+
+
+
+ )
}
export default App
diff --git a/src/components/error-boundary/index.tsx b/src/components/error-boundary/index.tsx
index a107b4e..74b4509 100644
--- a/src/components/error-boundary/index.tsx
+++ b/src/components/error-boundary/index.tsx
@@ -1,64 +1,65 @@
-import React, { ErrorInfo } from 'react'
import { Button, Result } from 'antd'
+import React, { ErrorInfo } from 'react'
export class ErrorBoundary extends React.Component<
- Record,
- { hasError: boolean; errorInfo: string }
+ Record,
+ { hasError: boolean; errorInfo: string }
> {
- state = { hasError: false, errorInfo: '' }
+ static getDerivedStateFromError(error: Error) {
+ return { hasError: true, errorInfo: error.message }
+ }
- static getDerivedStateFromError(error: Error) {
- return { hasError: true, errorInfo: error.message }
- }
+ componentDidCatch(error: any, errorInfo: ErrorInfo) {
+ // You can also log the error to an error reporting service
+ // eslint-disable-next-line no-console
+ console.log(error, errorInfo)
- componentDidCatch(error: any, errorInfo: ErrorInfo) {
- // You can also log the error to an error reporting service
- // eslint-disable-next-line no-console
- console.log(error, errorInfo)
- }
+ }
- render() {
- if (this.state.hasError) {
- // You can render any custom fallback UI
- return (
-
-
-
{this.state.errorInfo}
-
- {
- window.location.reload()
- }}
- >
- 刷新页面
-
- >
- }
- />
- )
- }
- return this.props.children
+ render() {
+ if (this.state.hasError) {
+ // You can render any custom fallback UI
+ return (
+
+
+
{this.state.errorInfo}
+
+ {
+ window.location.reload()
+ }}
+ >
+ 刷新页面
+
+ >
+ }
+ />
+ )
}
+ return this.props.children
+ }
+
+ state = { hasError: false, errorInfo: '' }
}
\ No newline at end of file
diff --git a/src/components/error/403.tsx b/src/components/error/403.tsx
new file mode 100644
index 0000000..34bfbfd
--- /dev/null
+++ b/src/components/error/403.tsx
@@ -0,0 +1,25 @@
+import { useNavigate } from '@tanstack/react-router'
+import { Button, Result } from 'antd'
+
+const NotPermission = () => {
+
+ const navigate = useNavigate()
+
+ return (
+ navigate({
+ to: '../'
+ })}>
+ Go Back
+
+ }
+ />
+ )
+}
+
+export default NotPermission
\ No newline at end of file
diff --git a/src/components/error/404.tsx b/src/components/error/404.tsx
new file mode 100644
index 0000000..9407ab3
--- /dev/null
+++ b/src/components/error/404.tsx
@@ -0,0 +1,25 @@
+import { useNavigate } from '@tanstack/react-router'
+import { Button, Result } from 'antd'
+
+const NotFound = () => {
+
+ const navigate = useNavigate()
+
+ return (
+ navigate({
+ to: '../'
+ })}>
+ Go Back
+
+ }
+ />
+ )
+}
+
+export default NotFound
\ No newline at end of file
diff --git a/src/components/error/error.tsx b/src/components/error/error.tsx
new file mode 100644
index 0000000..83c83b2
--- /dev/null
+++ b/src/components/error/error.tsx
@@ -0,0 +1,37 @@
+import { Result } from 'antd'
+
+
+const ErrorPage = ({ error }: { error: any, reset?: string }) => {
+ return (
+
+
+ >
+ }
+ />
+ )
+}
+
+export default ErrorPage
\ No newline at end of file
diff --git a/src/components/page-loading/index.tsx b/src/components/page-loading/index.tsx
new file mode 100644
index 0000000..72e5692
--- /dev/null
+++ b/src/components/page-loading/index.tsx
@@ -0,0 +1,13 @@
+import { Spin } from 'antd'
+
+const PageLoading = () => {
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default PageLoading
\ No newline at end of file
diff --git a/src/layout/EmptyLayout.tsx b/src/layout/EmptyLayout.tsx
index bac73dc..3190a4d 100644
--- a/src/layout/EmptyLayout.tsx
+++ b/src/layout/EmptyLayout.tsx
@@ -1,11 +1,15 @@
-import { Outlet } from '@tanstack/react-router'
+import ErrorPage from '@/components/error/error.tsx'
+import { CatchBoundary, Outlet } from '@tanstack/react-router'
const EmptyLayout = () => {
- return (
- <>
-
- >
- )
+ return (
+ 'reset-layout'}
+ errorComponent={ErrorPage}
+ >
+
+
+ )
}
export default EmptyLayout
\ No newline at end of file
diff --git a/src/layout/RootLayout.tsx b/src/layout/RootLayout.tsx
index c8259b7..a2f80c6 100644
--- a/src/layout/RootLayout.tsx
+++ b/src/layout/RootLayout.tsx
@@ -1,144 +1,147 @@
-import {
- ProConfigProvider,
- ProLayout,
-} from '@ant-design/pro-components'
+import PageBreadcrumb from '@/components/breadcrumb'
+import { ErrorBoundary } from '@/components/error-boundary'
+import ErrorPage from '@/components/error/error.tsx'
+import { MenuItem } from '@/types'
+import { ProConfigProvider, ProLayout, } from '@ant-design/pro-components'
+import { CatchBoundary, Link, Outlet, useRouteContext } from '@tanstack/react-router'
import { ConfigProvider, Dropdown } from 'antd'
import { useState } from 'react'
-import defaultProps from './_defaultProps'
-import { Link, Outlet, useRouteContext } from '@tanstack/react-router'
import Icon from '../components/icon'
-import { MenuItem } from '@/types'
-import PageBreadcrumb from '@/components/breadcrumb'
-import { ErrorBoundary } from '@/components/error-boundary'
+import defaultProps from './_defaultProps'
//根据menuData生成Breadcrumb所需的数据
const getBreadcrumbData = (menuData: MenuItem[], pathname: string) => {
- const breadcrumbData: any[] = []
- const findItem = (menuData: any[], pathname: string) => {
- for (let i = 0; i < menuData.length; i++) {
- if (menuData[i].path === pathname) {
- breadcrumbData.push(menuData[i])
- return true
- }
- if (menuData[i].children) {
- if (findItem(menuData[i].children, pathname)) {
- breadcrumbData.push(menuData[i])
- return true
- }
- }
+ const breadcrumbData: any[] = []
+ const findItem = (menuData: any[], pathname: string) => {
+ for (let i = 0; i < menuData.length; i++) {
+ if (menuData[i].path === pathname) {
+ breadcrumbData.push(menuData[i])
+ return true
+ }
+ if (menuData[i].children) {
+ if (findItem(menuData[i].children, pathname)) {
+ breadcrumbData.push(menuData[i])
+ return true
}
- return false
+ }
}
- findItem(menuData, pathname)
- return breadcrumbData.reverse()
+ return false
+ }
+ findItem(menuData, pathname)
+ return breadcrumbData.reverse()
}
export default () => {
- const { menuData } = useRouteContext({
- from: undefined,
- strict: false,
- select: (state) => state
- })
+ const { menuData } = useRouteContext({
+ from: undefined,
+ strict: false,
+ select: (state) => state
+ })
- const items = getBreadcrumbData(menuData, location.pathname)
+ const items = getBreadcrumbData(menuData, location.pathname)
- const [ pathname, setPathname ] = useState(location.pathname)
+ const [ pathname, setPathname ] = useState(location.pathname)
- return (
-
-
- {
- return document.getElementById('crazy-pro-layout') || document.body
- }}
+ return (
+
+
'reset-page'}
+ errorComponent={ErrorPage}
+ >
+
+ {
+ return document.getElementById('crazy-pro-layout') || document.body
+ }}
+ >
+ }
+ title="Crazy Pro"
+ {...defaultProps}
+ route={{
+ path: '/',
+ routes: menuData
+ }}
+ location={{
+ pathname,
+ }}
+ token={{
+ header: {
+ colorBgMenuItemSelected: 'rgba(0,0,0,0.04)',
+ },
+ }}
+ menu={{
+ collapsedShowGroupTitle: true,
+ }}
+ avatarProps={{
+ src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
+ size: 'small',
+ title: '管理员',
+ render: (_, dom) => {
+ return (
+ ,
+ label: '退出登录',
+ },
+ ],
+ }}
>
- }
- title="Crazy Pro"
- {...defaultProps}
- route={{
- path: '/',
- routes: menuData
- }}
- location={{
- pathname,
- }}
- token={{
- header: {
- colorBgMenuItemSelected: 'rgba(0,0,0,0.04)',
- },
- }}
- menu={{
- collapsedShowGroupTitle: true,
- }}
- avatarProps={{
- src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
- size: 'small',
- title: '管理员',
- render: (_, dom) => {
- return (
- ,
- label: '退出登录',
- },
- ],
- }}
- >
- {dom}
-
- )
- },
- }}
- actionsRender={(props) => {
- if (props.isMobile) return []
- if (typeof window === 'undefined') return []
- return []
- }}
- menuRender={(_, defaultDom) => (
- <>
- {defaultDom}
- >
- )}
- menuItemRender={(item, dom) => {
- return {
- setPathname(item.path || '/welcome')
- }}
- >
-
- {dom}
-
-
- }}
- {...{
- 'layout': 'mix',
- 'navTheme': 'light',
- 'contentWidth': 'Fluid',
- 'fixSiderbar': true,
- 'colorPrimary': '#1677FF',
- 'siderMenuType': 'group',
- // layout: 'side',
- }}
- ErrorBoundary={ErrorBoundary}
- >
-
-
-
-
-
- )
+ {dom}
+
+ )
+ },
+ }}
+ actionsRender={(props) => {
+ if (props.isMobile) return []
+ if (typeof window === 'undefined') return []
+ return []
+ }}
+ menuRender={(_, defaultDom) => (
+ <>
+ {defaultDom}
+ >
+ )}
+ menuItemRender={(item, dom) => {
+ return {
+ setPathname(item.path || '/welcome')
+ }}
+ >
+
+ {dom}
+
+
+ }}
+ {...{
+ 'layout': 'mix',
+ 'navTheme': 'light',
+ 'contentWidth': 'Fluid',
+ 'fixSiderbar': true,
+ 'colorPrimary': '#1677FF',
+ 'siderMenuType': 'group',
+ // layout: 'side',
+ }}
+ ErrorBoundary={ErrorBoundary}
+ >
+
+
+
+
+
+
+ )
}
diff --git a/src/layout/_authenticated.tsx b/src/layout/_authenticated.tsx
new file mode 100644
index 0000000..aeec104
--- /dev/null
+++ b/src/layout/_authenticated.tsx
@@ -0,0 +1,18 @@
+import { isAuthenticated } from '@/utils/auth.ts'
+import { createFileRoute,redirect } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/_authenticated')({
+ beforeLoad: async ({ location }) => {
+ if (!isAuthenticated()) {
+ throw redirect({
+ to: '/login',
+ search: {
+ // Use the current location to power a redirect after login
+ // (Do not use `router.state.resolvedLocation` as it can
+ // potentially lag behind the actual current location)
+ redirect: location.href,
+ },
+ })
+ }
+ },
+})
\ No newline at end of file
diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx
index 60fb8a4..764c09f 100644
--- a/src/pages/dashboard/index.tsx
+++ b/src/pages/dashboard/index.tsx
@@ -1,25 +1,25 @@
- import { ProCard } from '@ant-design/pro-components'
- import { createLazyRoute } from '@tanstack/react-router'
+import {ProCard} from '@ant-design/pro-components'
+import {createFileRoute} from '@tanstack/react-router'
const Index = () => {
return (
- <>
-
+ <>
+
- Dashboard
+ Dashboard
-
- >
+
+ >
)
}
- export const Route = createLazyRoute('/welcome')({
- component: Index,
- })
+export const Route = createFileRoute('/dashboard')({
+ component: Index,
+})
export default Index
\ No newline at end of file
diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx
index fafbe87..a2ab53e 100644
--- a/src/pages/login/index.tsx
+++ b/src/pages/login/index.tsx
@@ -1,15 +1,16 @@
import { createFileRoute } from '@tanstack/react-router'
const Login = () => {
- return (
-
- Login
-
- )
+
+ return (
+
+ {}
+
+ )
}
-export const Route = createFileRoute("/login")({
- component: Login
+export const Route = createFileRoute('/login')({
+ component: Login
})
export default Login
\ No newline at end of file
diff --git a/src/request.ts b/src/request.ts
index fdf661c..9ac6de4 100644
--- a/src/request.ts
+++ b/src/request.ts
@@ -1,6 +1,6 @@
-import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
-import { message } from 'antd'
import { getToken, setToken } from '@/store/system.ts'
+import { message } from 'antd'
+import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
export type { AxiosRequestConfig }
@@ -22,19 +22,10 @@ request.interceptors.request.use((config) => {
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
+}, (error) => {
+ console.log('error', error)
+ return Promise.reject(error)
})
diff --git a/src/routes.tsx b/src/routes.tsx
index c9cf98f..92b4e0d 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -1,239 +1,286 @@
+import NotPermission from '@/components/error/403.tsx'
+import NotFound from '@/components/error/404.tsx'
+import ErrorPage from '@/components/error/error.tsx'
+import PageLoading from '@/components/page-loading'
+import { Route as AuthenticatedImport } from '@/layout/_authenticated.tsx'
+import EmptyLayout from '@/layout/EmptyLayout.tsx'
+import ListPageLayout from '@/layout/ListPageLayout.tsx'
+import { Route as DashboardImport } from '@/pages/dashboard'
+import { Route as LoginRouteImport } from '@/pages/login'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import {
- createRouter,
- createRoute,
- RouterProvider, AnyRoute, redirect, createRootRouteWithContext, createLazyRoute, Outlet,
+ AnyRoute,
+ createLazyRoute,
+ createRootRouteWithContext,
+ createRoute,
+ createRouter,
+ Outlet,
+ redirect,
+ RouterProvider,
} from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools'
+import { memo } from 'react'
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 { appAtom, menuDataAtom } from './store/system.ts'
-import { useAtom } from 'jotai/index'
-import EmptyLayout from '@/layout/EmptyLayout.tsx'
export const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- }
+ defaultOptions: {
+ queries: {
+ retry: false,
}
+ }
})
const rootRoute = createRootRouteWithContext()({
- component: () => (
-
-
-
-
- ),
- beforeLoad: ({ location }) => {
- if (location.pathname === '/') {
- return redirect({ to: '/welcome' })
- }
- },
- notFoundComponent: () => 404 Not Found
,
+ component: () => (
+
+
+
+
+ ),
+ beforeLoad: ({ location }) => {
+ if (location.pathname === '/') {
+ return redirect({ to: '/dashboard' })
+ }
+ },
+ notFoundComponent: NotFound,
+ pendingComponent: PageLoading,
+ errorComponent: ({ error }) => ,
})
const emptyRoute = createRoute({
- getParentRoute: () => rootRoute,
- id: '/empty',
- component: EmptyLayout,
+ getParentRoute: () => rootRoute,
+ id: '/_empty',
+ component: EmptyLayout,
})
-const layoutRoute = createRoute({
- getParentRoute: () => rootRoute,
- id: '/layout',
- component: RootLayout,
+const authRoute = AuthenticatedImport.update({
+ getParentRoute: () => rootRoute,
+ id: '/_authenticated',
+} as any)
+
+const layoutNormalRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ id: '/_normal_layout',
+ component: RootLayout,
+})
+
+const layoutAuthRoute = createRoute({
+ getParentRoute: () => authRoute,
+ id: '/_auth_layout',
+ component: RootLayout,
})
+const notAuthRoute = createRoute({
+ getParentRoute: () => layoutNormalRoute,
+ path: '/not-auth',
+ component: NotPermission
+})
+
+const dashboardRoute = DashboardImport.update({
+ path: '/dashboard',
+ getParentRoute: () => layoutAuthRoute,
+} as any)
+
const loginRoute = LoginRouteImport.update({
- path: '/login',
- getParentRoute: () => emptyRoute,
+ path: '/login',
+ getParentRoute: () => emptyRoute,
} as any)
const menusRoute = createRoute({
- getParentRoute: () => layoutRoute,
- path: '/system/menus',
+ getParentRoute: () => layoutAuthRoute,
+ path: '/system/menus',
}).lazy(async () => await import('@/pages/system/menus').then(d => d.Route))
const departmentsRoute = createRoute({
- getParentRoute: () => layoutRoute,
- path: '/system/departments',
+ getParentRoute: () => layoutAuthRoute,
+ path: '/system/departments',
}).lazy(async () => await import('@/pages/system/departments').then(d => d.Route))
const usersRoute = createRoute({
- getParentRoute: () => layoutRoute,
- path: '/system/users',
+ getParentRoute: () => layoutAuthRoute,
+ path: '/system/users',
}).lazy(async () => await import('@/pages/system/users').then(d => d.Route))
const rolesRoute = createRoute({
- getParentRoute: () => layoutRoute,
- path: '/system/roles',
+ getParentRoute: () => layoutAuthRoute,
+ 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
- },
- }
+ interface FileRoutesByPath {
+ '/_authenticated': {
+ preLoaderRoute: typeof AuthenticatedImport
+ parentRoute: typeof rootRoute
+ },
+ '/_normal_layout': {
+ preLoaderRoute: typeof layoutNormalRoute
+ parentRoute: typeof rootRoute
+ },
+ '/_layout': {
+ preLoaderRoute: typeof layoutAuthRoute
+ parentRoute: typeof rootRoute
+ },
+ '/': {
+ preLoaderRoute: typeof DashboardImport
+ parentRoute: typeof layoutAuthRoute
+ },
+ '/dashboard': {
+ preLoaderRoute: typeof DashboardImport
+ parentRoute: typeof layoutAuthRoute
+ },
+ '/login': {
+ preLoaderRoute: typeof LoginRouteImport
+ parentRoute: typeof rootRoute
+ },
+ '/system/menus': {
+ preLoaderRoute: typeof menusRoute
+ parentRoute: typeof layoutAuthRoute
+ },
+ '/system/departments': {
+ preLoaderRoute: typeof departmentsRoute
+ parentRoute: typeof layoutAuthRoute
+ },
+ '/system/users': {
+ preLoaderRoute: typeof usersRoute
+ parentRoute: typeof layoutAuthRoute
+ },
+ '/system/roles': {
+ preLoaderRoute: typeof rolesRoute
+ parentRoute: typeof layoutAuthRoute
+ },
+ '/welcome': {
+ preLoaderRoute: typeof rootRoute
+ parentRoute: typeof layoutAuthRoute
+ },
+ }
}
const routeTree = rootRoute.addChildren(
- [
- loginRoute,
- emptyRoute,
- layoutRoute.addChildren(
- [
- menusRoute,
- departmentsRoute,
- usersRoute,
- rolesRoute,
- ]
- ),
- ]
+ [
+ //非Layout
+ loginRoute,
+ emptyRoute,
+
+ //不带权限Layout
+ layoutNormalRoute.addChildren([
+ notAuthRoute,
+ ]),
+
+ //带权限Layout
+ dashboardRoute,
+ authRoute.addChildren(
+ [
+ layoutAuthRoute.addChildren(
+ [
+ menusRoute,
+ departmentsRoute,
+ usersRoute,
+ rolesRoute,
+ ]
+ ),
+ ]
+ )
+ ]
)
export const generateDynamicRoutes = (menuData: MenuItem[]) => {
- // 递归生成路由,如果有routes则递归生成子路由
+ // 递归生成路由,如果有routes则递归生成子路由
- const generateRoutes = (menu: MenuItem, parentRoute: AnyRoute) => {
+ const generateRoutes = (menu: MenuItem, parentRoute: AnyRoute) => {
- const path = menu.path?.replace(parentRoute.options?.path, '')
- const isLayout = menu.children && menu.children.length > 0 && menu.type === 'menu'
+ const path = menu.path?.replace(parentRoute.options?.path, '')
+ const isLayout = menu.children && menu.children.length > 0 && menu.type === 'menu'
- if (isLayout && !menu.component) {
- //没有component的layout,直接返回
- return createRoute({
- getParentRoute: () => parentRoute,
- id: path!,
- component: ListPageLayout,
- })
- }
+ if (isLayout && !menu.component) {
+ //没有component的layout,直接返回
+ return createRoute({
+ getParentRoute: () => parentRoute,
+ id: path!,
+ component: ListPageLayout,
+ })
+ }
- // @ts-ignore 添加menu属性,方便后面获取
- const options = {
- getParentRoute: () => parentRoute,
- menu,
- } as any
+ // @ts-ignore 添加menu属性,方便后面获取
+ const options = {
+ getParentRoute: () => parentRoute,
+ menu,
+ } as any
- if (isLayout) {
- options.id = path!
- } else {
- options.path = path!
- }
+ if (isLayout) {
+ options.id = path!
+ } else {
+ options.path = path!
+ }
- //删除掉parentRoute的path,避免重复
- const route = createRoute(options).lazy(async () => {
-
- // @ts-ignore 获取route中的menu属性
- const menu = route.options.menu as MenuItem
- let component = menu.component
-
- // menu.type
- // 1,组件(页面),2,IFrame,3,外链接,4,按钮
- if (menu.type === 'iframe') {
- component = '@/components/Iframe'
- }
-
- if (!component) {
- return createLazyRoute(menu.path)({
- component: () => 404 Not Found
- })
- }
- /* @vite-ignore */
- const d = await import(`${component}`)
- if (d.Route) {
- return d.Route
- }
- if (d.GenRoute) {
- return d.GenRoute(menu.path)
- }
- return createLazyRoute(menu.path)({
- component: d.default || d
- })
- })
- return route
+ //删除掉parentRoute的path,避免重复
+ const route = createRoute(options).lazy(async () => {
- }
+ // @ts-ignore 获取route中的menu属性
+ const menu = route.options.menu as MenuItem
+ let component = menu.component
+
+ // menu.type
+ // 1,组件(页面),2,IFrame,3,外链接,4,按钮
+ if (menu.type === 'iframe') {
+ component = '@/components/Iframe'
+ }
- // 对menuData递归生成路由,只处理type =1 的菜单
- const did = (menus: MenuItem[], parentRoute: AnyRoute) => {
- return menus.filter((item) => item.type === 'menu').map((item) => {
- // 如果有children则递归生成子路由,同样只处理type =1 的菜单
- const route = generateRoutes(item, parentRoute)
- if (item.children && item.children.length > 0) {
- const children = did(item.children, route)
- if (children.length > 0) {
- route.addChildren(children)
- }
- }
- return route
+ if (!component) {
+ return createLazyRoute(menu.path)({
+ component: () => 404 Not Found
})
- }
+ }
+ /* @vite-ignore */
+ const d = await import(`${component}`)
+ if (d.Route) {
+ return d.Route
+ }
+ if (d.GenRoute) {
+ return d.GenRoute(menu.path)
+ }
+ return createLazyRoute(menu.path)({
+ component: d.default || d
+ })
+ })
+ return route
+
+ }
+
+ // 对menuData递归生成路由,只处理type =1 的菜单
+ const did = (menus: MenuItem[], parentRoute: AnyRoute) => {
+ return menus.filter((item) => item.type === 'menu').map((item) => {
+ // 如果有children则递归生成子路由,同样只处理type =1 的菜单
+ const route = generateRoutes(item, parentRoute)
+ if (item.children && item.children.length > 0) {
+ const children = did(item.children, route)
+ if (children.length > 0) {
+ route.addChildren(children)
+ }
+ }
+ return route
+ })
+ }
- return did(menuData, rootRoute)
+ return did(menuData, rootRoute)
}
const router = createRouter({
- routeTree,
- context: { queryClient, menuData: undefined },
- defaultPreload: 'intent'
+ routeTree,
+ context: { queryClient, menuData: [] },
+ defaultPreload: 'intent'
})
-export const RootProvider = () => {
-
- const [ , ] = useAtom(appAtom)
- const { data, isError, isPending } = useAtomValue(menuDataAtom)
-
- if (isError) {
- return Error
- }
+export const RootProvider = memo((props: { context: Partial }) => {
- if (isPending) {
- return Loading...
- }
-
- router.update({
- context: {
- queryClient,
- menuData: data,
- }
- })
-
- return (
-
-
-
- )
-}
\ No newline at end of file
+ return (
+
+
+
+ )
+})
\ No newline at end of file
diff --git a/src/store/system.ts b/src/store/system.ts
index f098361..4ac42da 100644
--- a/src/store/system.ts
+++ b/src/store/system.ts
@@ -1,9 +1,10 @@
-import { atomWithQuery } from 'jotai-tanstack-query'
-import systemServ from '../service/system.ts'
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 { formatMenuData } from '@/utils'
+import systemServ from '../service/system.ts'
/**
@@ -33,7 +34,9 @@ export const setToken = (token: string) => {
export const menuDataAtom = atomWithQuery(() => ({
queryKey: [ 'menus' ],
queryFn: async () => {
- if (window.location.pathname === '/login') return []
+ if (!isAuthenticated()) {
+ return []
+ }
return await systemServ.menus.list()
},
select: data => formatMenuData(data as any ?? []),
diff --git a/src/types.d.ts b/src/types.d.ts
index e00ff23..b4b4822 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -1,8 +1,8 @@
-import { Attributes, ReactNode } from 'react'
+import { IMenu } from '@/types/menus'
import { QueryClient } from '@tanstack/react-query'
import { Router } from '@tanstack/react-router'
import { RouteOptions } from '@tanstack/react-router/src/route.ts'
-import { IMenu } from '@/types/menus'
+import { Attributes, ReactNode } from 'react'
export type LayoutType = 'list' | 'form' | 'tree' | 'normal'
@@ -38,11 +38,11 @@ export type Props = Attributes & {
};
export interface IRootContext {
- menuData?: MenuItem[];
+ menuData: MenuItem[];
queryClient: QueryClient;
}
-interface MenuItem extends IMenu{
+interface MenuItem extends IMenu {
routes?: MenuItem[];
}
diff --git a/src/utils/auth.ts b/src/utils/auth.ts
new file mode 100644
index 0000000..6ee3dd9
--- /dev/null
+++ b/src/utils/auth.ts
@@ -0,0 +1,7 @@
+import { getToken } from '@/store/system.ts'
+
+
+export const isAuthenticated = () => {
+ const token = getToken()
+ return !!token
+}
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index d4c409a..99651fe 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2068,6 +2068,27 @@ hoist-non-react-statics@^3.3.2:
dependencies:
react-is "^16.7.0"
+html-parse-stringify@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
+ integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
+ dependencies:
+ void-elements "3.1.0"
+
+i18next-browser-languagedetector@^7.2.1:
+ version "7.2.1"
+ resolved "https://registry.npmmirror.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.1.tgz#1968196d437b4c8db847410c7c33554f6c448f6f"
+ integrity sha512-h/pM34bcH6tbz8WgGXcmWauNpQupCGr25XPp9cZwZInR9XHSjIFDYp1SIok7zSPsTOMxdvuLyu86V+g2Kycnfw==
+ dependencies:
+ "@babel/runtime" "^7.23.2"
+
+i18next@^23.11.2:
+ version "23.11.2"
+ resolved "https://registry.npmmirror.com/i18next/-/i18next-23.11.2.tgz#4c0e8192a9ba230fe7dc68b76459816ab601826e"
+ integrity sha512-qMBm7+qT8jdpmmDw/kQD16VpmkL9BdL+XNAK5MNbNFaf1iQQq35ZbPrSlqmnNPOSUY4m342+c0t0evinF5l7sA==
+ dependencies:
+ "@babel/runtime" "^7.23.2"
+
ignore@^5.2.0, ignore@^5.3.1:
version "5.3.1"
resolved "https://registry.npmmirror.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
@@ -2887,6 +2908,14 @@ react-dom@^18.2.0:
loose-envify "^1.1.0"
scheduler "^0.23.0"
+react-i18next@^14.1.0:
+ version "14.1.0"
+ resolved "https://registry.npmmirror.com/react-i18next/-/react-i18next-14.1.0.tgz#44da74fbffd416f5d0c5307ef31735cf10cc91d9"
+ integrity sha512-3KwX6LHpbvGQ+sBEntjV4sYW3Zovjjl3fpoHbUwSgFHf0uRBcbeCBLR5al6ikncI5+W0EFb71QXZmfop+J6NrQ==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ html-parse-stringify "^3.0.1"
+
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -3231,6 +3260,11 @@ vite@^5.2.0:
optionalDependencies:
fsevents "~2.3.3"
+void-elements@3.1.0:
+ version "3.1.0"
+ resolved "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
+ integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
+
warning@^4.0.3:
version "4.0.3"
resolved "https://registry.npmmirror.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"