From 6e94f1cd04362eceebe80eaa86303196d6cace32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=87=91?= Date: Thu, 18 Apr 2024 15:20:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 5 +- src/App.tsx | 24 +- src/components/error-boundary/index.tsx | 111 ++++----- src/components/error/403.tsx | 25 ++ src/components/error/404.tsx | 25 ++ src/components/error/error.tsx | 37 +++ src/components/page-loading/index.tsx | 13 ++ src/layout/EmptyLayout.tsx | 16 +- src/layout/RootLayout.tsx | 255 ++++++++++---------- src/layout/_authenticated.tsx | 18 ++ src/pages/dashboard/index.tsx | 30 +-- src/pages/login/index.tsx | 15 +- src/request.ts | 19 +- src/routes.tsx | 403 ++++++++++++++++++-------------- src/store/system.ts | 11 +- src/types.d.ts | 8 +- src/utils/auth.ts | 7 + yarn.lock | 34 +++ 18 files changed, 639 insertions(+), 417 deletions(-) create mode 100644 src/components/error/403.tsx create mode 100644 src/components/error/404.tsx create mode 100644 src/components/error/error.tsx create mode 100644 src/components/page-loading/index.tsx create mode 100644 src/layout/_authenticated.tsx create mode 100644 src/utils/auth.ts 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}

-
- - - } - /> - ) - } - return this.props.children + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + return ( + +
+

{this.state.errorInfo}

+
+ + + } + /> + ) } + 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 ( + +
+

{error?.message}

+
+ + } + /> + ) +} + +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"