You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
10 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
12 months ago
1 year ago
1 year ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
12 months ago
11 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
11 months ago
1 year ago
12 months ago
12 months ago
11 months ago
12 months ago
1 year ago
1 year ago
12 months ago
1 year ago
1 year ago
1 year ago
12 months ago
12 months ago
12 months ago
  1. import NotPermission from '@/components/error/403.tsx'
  2. import NotFound from '@/components/error/404.tsx'
  3. import ErrorPage from '@/components/error/error.tsx'
  4. import Loading from '@/components/loading'
  5. import FetchLoading from '@/components/loading/FetchLoading.tsx'
  6. import PageLoading from '@/components/page-loading'
  7. import { PageStoreProvider } from '@/store'
  8. import { AuthenticatedRoute as AuthenticatedImport } from './_authenticatedRoute.tsx'
  9. import EmptyLayout from '@/layout/EmptyLayout.tsx'
  10. // import ListPageLayout from '@/layout/ListPageLayout.tsx'
  11. // import { Route as DashboardImport } from '@/pages/dashboard'
  12. import { Route as LoginRouteImport } from '@/pages/login'
  13. import { generateUUID } from '@/utils/uuid.ts'
  14. import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
  15. import {
  16. AnyRoute,
  17. createRootRouteWithContext,
  18. createRoute,
  19. createRouter, lazyRouteComponent,
  20. Outlet,
  21. redirect,
  22. RouterProvider,
  23. // createHashHistory,
  24. } from '@tanstack/react-router'
  25. // import { TanStackRouterDevtools } from '@tanstack/router-devtools'
  26. import { memo, useEffect, useRef } from 'react'
  27. import RootLayout from './layout/RootLayout'
  28. import { IRootContext, MenuItem } from './global'
  29. // import { DevTools } from 'jotai-devtools'
  30. import { useAtomValue } from 'jotai'
  31. import { userMenuDataAtom } from '@/store/system/user.ts'
  32. const PageRootLayout = () => {
  33. return <PageStoreProvider>
  34. <RootLayout/>
  35. </PageStoreProvider>
  36. }
  37. export const queryClient = new QueryClient({
  38. defaultOptions: {
  39. queries: {
  40. retry: false,
  41. }
  42. }
  43. })
  44. const rootRoute = createRootRouteWithContext<IRootContext>()({
  45. component: () => (
  46. <>
  47. <FetchLoading/>
  48. <Outlet/>
  49. {/*<DevTools/>*/}
  50. {/*<TanStackRouterDevtools position={'bottom-right'}/>*/}
  51. </>
  52. ),
  53. beforeLoad: ({ location }) => {
  54. if (location.pathname === '/') {
  55. return redirect({ to: '/dashboard' })
  56. }
  57. },
  58. loader: () => {
  59. },
  60. notFoundComponent: NotFound,
  61. pendingComponent: PageLoading,
  62. errorComponent: ({ error }) => <ErrorPage error={error}/>,
  63. })
  64. const emptyRoute = createRoute({
  65. getParentRoute: () => rootRoute,
  66. id: '/_empty',
  67. component: EmptyLayout,
  68. })
  69. const authRoute = AuthenticatedImport.update({
  70. getParentRoute: () => rootRoute,
  71. id: '/_authenticated',
  72. } as any)
  73. const layoutNormalRoute = createRoute({
  74. getParentRoute: () => rootRoute,
  75. id: '/_normal_layout',
  76. component: PageRootLayout,
  77. })
  78. const layoutAuthRoute = createRoute({
  79. getParentRoute: () => authRoute,
  80. id: '/_auth_layout',
  81. component: PageRootLayout,
  82. })
  83. const notAuthRoute = createRoute({
  84. getParentRoute: () => layoutNormalRoute,
  85. path: '/not-auth',
  86. component: NotPermission
  87. })
  88. // const dashboardRoute = DashboardImport.update({
  89. // path: '/dashboard',
  90. // getParentRoute: () => layoutAuthRoute,
  91. // } as any)
  92. const loginRoute = LoginRouteImport.update({
  93. path: '/login',
  94. getParentRoute: () => emptyRoute,
  95. } as any)
  96. //
  97. // const menusRoute = createRoute({
  98. // getParentRoute: () => layoutAuthRoute,
  99. // path: '/system/menus',
  100. // }).lazy(async () => await import('@/pages/system/menus').then(d => d.Route))
  101. //
  102. // const departmentsRoute = createRoute({
  103. // getParentRoute: () => layoutAuthRoute,
  104. // path: '/system/departments',
  105. // }).lazy(async () => await import('@/pages/system/departments').then(d => d.Route))
  106. //
  107. // const usersRoute = createRoute({
  108. // getParentRoute: () => layoutAuthRoute,
  109. // path: '/system/users',
  110. // }).lazy(async () => await import('@/pages/system/users').then(d => d.Route))
  111. //
  112. // const rolesRoute = createRoute({
  113. // getParentRoute: () => layoutAuthRoute,
  114. // path: '/system/roles',
  115. // }).lazy(async () => await import('@/pages/system/roles').then(d => d.Route))
  116. declare module '@tanstack/react-router' {
  117. interface FileRoutesByPath {
  118. '/_authenticated': {
  119. preLoaderRoute: typeof AuthenticatedImport
  120. parentRoute: typeof rootRoute
  121. },
  122. '/_normal_layout': {
  123. preLoaderRoute: typeof layoutNormalRoute
  124. parentRoute: typeof rootRoute
  125. },
  126. '/_layout': {
  127. preLoaderRoute: typeof layoutAuthRoute
  128. parentRoute: typeof rootRoute
  129. },
  130. // '/': {
  131. // preLoaderRoute: typeof DashboardImport
  132. // parentRoute: typeof layoutAuthRoute
  133. // },
  134. // '/dashboard': {
  135. // preLoaderRoute: typeof DashboardImport
  136. // parentRoute: typeof layoutAuthRoute
  137. // },
  138. '/login': {
  139. preLoaderRoute: typeof LoginRouteImport
  140. parentRoute: typeof rootRoute
  141. },
  142. // '/system/menus': {
  143. // preLoaderRoute: typeof menusRoute
  144. // parentRoute: typeof layoutAuthRoute
  145. // },
  146. // '/system/departments': {
  147. // preLoaderRoute: typeof departmentsRoute
  148. // parentRoute: typeof layoutAuthRoute
  149. // },
  150. // '/system/users': {
  151. // preLoaderRoute: typeof usersRoute
  152. // parentRoute: typeof layoutAuthRoute
  153. // },
  154. // '/system/roles': {
  155. // preLoaderRoute: typeof rolesRoute
  156. // parentRoute: typeof layoutAuthRoute
  157. // },
  158. '/welcome': {
  159. preLoaderRoute: typeof rootRoute
  160. parentRoute: typeof layoutAuthRoute
  161. },
  162. }
  163. }
  164. const modules = import.meta.glob('./pages/**/*.{jsx,tsx}')
  165. console.log(modules)
  166. const generateDynamicRoutes = (menuData: MenuItem[], parentRoute: AnyRoute) => {
  167. // 递归生成路由,如果有routes则递归生成子路由
  168. const generateRoutes = (menu: MenuItem, parentRoute: AnyRoute) => {
  169. const path = menu.path?.replace(parentRoute.options?.path, '')
  170. const isLayout = menu.children && menu.children.length > 0 && menu.type === 'menu'
  171. if (isLayout && (!menu.path || !menu.component)) {
  172. //没有component的layout,直接返回
  173. return createRoute({
  174. getParentRoute: () => layoutAuthRoute,
  175. id: `/layout-no-path-${generateUUID()}`,
  176. component: EmptyLayout,
  177. })
  178. }
  179. // @ts-ignore 添加menu属性,方便后面获取
  180. const options = {
  181. getParentRoute: () => parentRoute,
  182. menu,
  183. } as any
  184. if (isLayout) {
  185. options.id = path ?? `/layout-${generateUUID()}`
  186. } else {
  187. if (!path) {
  188. console.log(`${menu.name}没有设置视图`)
  189. options.id = menu.meta.name
  190. } else {
  191. options.path = path
  192. }
  193. }
  194. let component = menu.component
  195. // menu.type
  196. // 1,组件(页面),2,IFrame,3,外链接,4,按钮
  197. if (menu.type === 'iframe') {
  198. component = '@/components/Iframe'
  199. }
  200. //处理component路径
  201. component = component.replace(/^\/pages/, '')
  202. component = component.replace(/^\//, '')
  203. return createRoute({
  204. ...options,
  205. // @ts-ignore fix import
  206. component: lazyRouteComponent(() => {
  207. //处理最后可能包含index || index.tsx || index.jsx
  208. if (component.endsWith('.tsx')) {
  209. component = component.replace(/\.tsx$/, '')
  210. }
  211. if (component.endsWith('.jsx')) {
  212. component = component.replace(/\.jsx$/, '')
  213. }
  214. if (component.endsWith('/index')) {
  215. component = component.replace(/\/index$/, '')
  216. }
  217. let module: () => Promise<any>
  218. //优先匹配无index的情况
  219. if (modules[`./pages/${component}.tsx`] || modules[`./pages/${component}.jsx`]) {
  220. module = modules[`./pages/${component}.tsx`] || modules[`./pages/${component}.jsx`]
  221. } else {
  222. module = modules[`./pages/${component}/index.tsx`] || modules[`./pages/${component}/index.jsx`]
  223. }
  224. if (!module) {
  225. return NotFound
  226. }
  227. return module().then((d: any) => {
  228. // console.log(d)
  229. if (d.Route) {
  230. d.Route.update({
  231. path: menu.path,
  232. })
  233. }
  234. return d
  235. })
  236. }),
  237. notFoundComponent: NotFound,
  238. })
  239. }
  240. // 对menuData递归生成路由,只处理type =1 的菜单
  241. const did = (menus: MenuItem[], parentRoute: AnyRoute) => {
  242. return menus.filter((item) => item.type === 'menu').map((item, index) => {
  243. // 如果有children则递归生成子路由,同样只处理type =1 的菜单
  244. const route = generateRoutes(item, parentRoute)
  245. // console.log(route)
  246. if (item.children && item.children.length > 0) {
  247. const children = did(item.children, route)
  248. if (children.length > 0) {
  249. route.addChildren(children)
  250. }
  251. }
  252. route.init({ originalIndex: index })
  253. return route
  254. })
  255. }
  256. const routes = did(menuData, parentRoute)
  257. parentRoute.addChildren(routes)
  258. }
  259. const routeTree = rootRoute.addChildren(
  260. [
  261. //非Layout
  262. loginRoute,
  263. emptyRoute,
  264. //不带权限Layout
  265. layoutNormalRoute.addChildren([
  266. notAuthRoute,
  267. ]),
  268. //带权限Layout
  269. // dashboardRoute,
  270. authRoute.addChildren(
  271. [
  272. layoutAuthRoute
  273. /*.addChildren(
  274. [
  275. menusRoute,
  276. departmentsRoute,
  277. usersRoute,
  278. rolesRoute,
  279. ]
  280. ),*/
  281. ]),
  282. ]
  283. )
  284. export const RootProvider = memo((props: { context: Partial<IRootContext> }) => {
  285. const { data: menuData, isLoading, refetch } = useAtomValue(userMenuDataAtom)
  286. const isFetchRef = useRef(false)
  287. useEffect(() => {
  288. if (isFetchRef.current) {
  289. return
  290. }
  291. isFetchRef.current = true
  292. refetch()
  293. }, [])
  294. if (isLoading) {
  295. return <PageLoading/>
  296. }
  297. generateDynamicRoutes(menuData ?? [], layoutAuthRoute)
  298. // const hashHistory = createHashHistory()
  299. const router = createRouter({
  300. routeTree,
  301. // history: hashHistory,
  302. context: { queryClient, menuData: [] },
  303. defaultPreload: 'intent',
  304. defaultPendingComponent: () => <Loading loading={true} delay={100}/>
  305. })
  306. return (
  307. <QueryClientProvider client={queryClient}>
  308. <RouterProvider router={router} context={{ ...props.context, menuData, queryClient }}/>
  309. </QueryClientProvider>
  310. )
  311. })
  312. export default RootProvider