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.

227 lines
9.1 KiB

1 year ago
1 year ago
1 year ago
12 months ago
1 year 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
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
12 months ago
12 months ago
1 year ago
12 months ago
1 year ago
1 year ago
  1. import Avatar from '@/components/avatar'
  2. import PageBreadcrumb from '@/components/breadcrumb'
  3. import ErrorPage from '@/components/error/error.tsx'
  4. import SelectLang from '@/components/select-lang'
  5. import { appAtom } from '@/store/system.ts'
  6. import { userMenuDataAtom } from '@/store/system/user.ts'
  7. import { MenuItem } from '@/global'
  8. import { ProConfigProvider, ProLayout, } from '@ant-design/pro-components'
  9. import { zhCNIntl, enUSIntl } from '@ant-design/pro-provider/es/intl'
  10. import { CatchBoundary, Link, Outlet } from '@tanstack/react-router'
  11. import { ConfigProvider } from '@/components/config-provider'
  12. import { useEffect, useRef, useState } from 'react'
  13. import { useAtomValue } from 'jotai'
  14. import { useStyle } from '@/layout/style.ts'
  15. import zh from 'antd/locale/zh_CN'
  16. import en from 'antd/locale/en_US'
  17. import type { MenuDataItem } from '@ant-design/pro-layout/es/typing'
  18. import { flattenTree } from '@/utils'
  19. //根据menuData生成Breadcrumb所需的数据
  20. const getBreadcrumbData = (menuData: MenuItem[], pathname: string) => {
  21. const breadcrumbData: any[] = []
  22. const findItem = (menuData: any[], pathname: string) => {
  23. for (let i = 0; i < menuData.length; i++) {
  24. if (menuData[i].path === pathname) {
  25. breadcrumbData.push(menuData[i])
  26. return true
  27. }
  28. if (menuData[i].children) {
  29. if (findItem(menuData[i].children, pathname)) {
  30. breadcrumbData.push(menuData[i])
  31. return true
  32. }
  33. }
  34. }
  35. return false
  36. }
  37. findItem(menuData, pathname)
  38. return breadcrumbData.reverse()
  39. }
  40. export default () => {
  41. const { styles } = useStyle()
  42. const { data: menuData = [], isLoading } = useAtomValue(userMenuDataAtom)
  43. const { language } = useAtomValue(appAtom)
  44. const items = getBreadcrumbData(menuData, location.pathname)
  45. const [ pathname, setPathname ] = useState(location.pathname)
  46. const menusFlatten = useRef<MenuItem[]>()
  47. if (!menusFlatten.current) {
  48. menusFlatten.current = flattenTree<MenuItem>(menuData, { key: 'id', title: 'name' })
  49. }
  50. const [ rootMenuKeys, setRootMenuKeys ] = useState<string[]>(() => {
  51. const item = menusFlatten.current?.find(item => item.path === location.pathname)
  52. return item ? item.parentName : []
  53. })
  54. const childMenuRef = useRef<MenuItem[]>([])
  55. childMenuRef.current = menuData.find(item => {
  56. return item.key === rootMenuKeys?.[0]
  57. })?.children || []
  58. useEffect(() => {
  59. const item = menusFlatten.current?.find(item => item.path === location.pathname)
  60. if (item && item.key !== rootMenuKeys?.[0]) {
  61. setRootMenuKeys(item.parentName)
  62. }
  63. }, [ location.pathname ])
  64. return (
  65. <div
  66. className={styles.container}
  67. id="crazy-pro-layout"
  68. style={{
  69. height: '100vh',
  70. // overflow: 'auto',
  71. }}
  72. >
  73. <CatchBoundary
  74. getResetKey={() => 'reset-page'}
  75. errorComponent={ErrorPage}
  76. >
  77. <ProConfigProvider hashed={false} intl={language === 'zh-CN' ? zhCNIntl : enUSIntl}>
  78. <ConfigProvider
  79. locale={language === 'zh-CN' ? zh : en}
  80. getTargetContainer={() => {
  81. return document.getElementById('crazy-pro-layout') || document.body
  82. }}
  83. >
  84. <ProLayout
  85. token={{
  86. header: {
  87. colorBgMenuItemSelected: 'rgba(0,0,0,0.04)',
  88. },
  89. }}
  90. fixedHeader={true}
  91. headerContentRender={() => <PageBreadcrumb
  92. className={'top-breadcrumb'}
  93. showIcon={false}
  94. items={items}/>}
  95. title="Crazy Pro"
  96. layout={'mix'}
  97. fixSiderbar={true}
  98. siderWidth={100}
  99. collapsedButtonRender={false}
  100. // collapsed={false}
  101. postMenuData={() => {
  102. return menuData.map(item => ({
  103. ...item,
  104. children: [],
  105. })) as any
  106. }}
  107. route={
  108. {
  109. path: '/',
  110. routes: menuData.map(item => ({
  111. ...item,
  112. // path: item.path ?? `/${item.key}`,
  113. children: [],
  114. // routes: undefined
  115. }))
  116. }
  117. }
  118. location={
  119. {
  120. pathname,
  121. }
  122. }
  123. menu={{
  124. collapsedShowGroupTitle: true,
  125. }}
  126. menuItemRender={(item: MenuDataItem) => {
  127. return <span style={{ userSelect: 'none' }} onClick={() => {
  128. setRootMenuKeys([ (item as any).key || 'dashboard' ])
  129. setPathname(item.path || '/dashboard')
  130. }}
  131. >
  132. <Link to={item.path} className={'menu-link'} target={item.type === 'url' ? '_blank' : '_self'}>
  133. <span>{item.icon}</span>
  134. <span>{item.name}</span>
  135. </Link>
  136. </span>
  137. }}
  138. avatarProps={{
  139. // src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
  140. render: () => {
  141. return (
  142. <Avatar/>
  143. )
  144. },
  145. }}
  146. actionsRender={(props) => {
  147. if (props.isMobile) return []
  148. if (typeof window === 'undefined') return []
  149. return [
  150. <SelectLang/>,
  151. ]
  152. }}
  153. menuProps={{
  154. className: styles.mySiderMenu,
  155. selectedKeys: rootMenuKeys,
  156. }}
  157. // navTheme={'light'}
  158. contentStyle={{ paddingBlock: 0, paddingInline: 0 }}
  159. >
  160. <ProLayout
  161. className={styles.mySider}
  162. headerRender={false}
  163. hasSiderMenu={false}
  164. postMenuData={() => {
  165. return (childMenuRef.current || []) as any
  166. }}
  167. route={{
  168. path: '/',
  169. routes: menuData
  170. }}
  171. location={{
  172. pathname,
  173. }}
  174. token={{
  175. header: {
  176. colorBgMenuItemSelected: 'rgba(0,0,0,0.04)',
  177. },
  178. }}
  179. menuProps={{
  180. className: styles.sideMenu,
  181. }}
  182. menu={{
  183. hideMenuWhenCollapsed: false,
  184. // collapsedShowGroupTitle: true,
  185. loading: isLoading,
  186. }}
  187. menuRender={childMenuRef.current?.length ? undefined : false}
  188. menuItemRender={(item, dom) => {
  189. return <span style={{ userSelect: 'none' }} onClick={() => {
  190. setPathname(item.path || '/dashboard')
  191. }}
  192. >
  193. <Link to={item.path} target={item.type === 'url' ? '_blank' : '_self'}>
  194. {dom}
  195. </Link>
  196. </span>
  197. }}
  198. {...{
  199. 'layout': 'mix',
  200. 'navTheme': 'light',
  201. 'contentWidth': 'Fluid',
  202. 'fixSiderbar': false,
  203. // 'colorPrimary': '#1677FF',
  204. // 'siderMenuType': 'group',
  205. // layout: 'side',
  206. }}
  207. >
  208. <Outlet/>
  209. </ProLayout>
  210. </ProLayout>
  211. </ConfigProvider>
  212. </ProConfigProvider>
  213. </CatchBoundary>
  214. </div>
  215. )
  216. }