react-toolkits 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-toolkits",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "sideEffects": [
5
5
  "**/*.css"
6
6
  ],
@@ -39,7 +39,6 @@
39
39
  "@types/react": "^18.2.16",
40
40
  "@types/react-dom": "^18.2.7",
41
41
  "autoprefixer": "^10.4.14",
42
- "eslint-import-resolver-typescript": "^3.5.5",
43
42
  "postcss": "^8.4.27",
44
43
  "prettier": "^3.0.0",
45
44
  "react": "^18.2.0",
@@ -48,7 +47,7 @@
48
47
  "ts-essentials": "^9.3.2",
49
48
  "tsup": "^7.1.0",
50
49
  "typescript": "^5.1.6",
51
- "@flow97/eslint-config-mono": "0.0.1",
50
+ "@flow97/eslint-config-mono": "0.0.2",
52
51
  "tailwind-config": "0.0.1",
53
52
  "tsconfig": "0.0.1"
54
53
  },
@@ -5,21 +5,21 @@ export interface PermissionCheckResult {
5
5
  [k: string]: boolean
6
6
  }
7
7
 
8
- export function usePermissions(codes: Record<string, string>) {
8
+ export function usePermissions(codes: string[]) {
9
9
  const fetcher = useFetcher()
10
10
 
11
11
  const { data, isLoading } = useSWRImmutable(
12
- Object.keys(codes).length > 0
12
+ codes.length > 0
13
13
  ? {
14
14
  method: 'POST',
15
15
  url: '/api/usystem/user/check',
16
- data: { permissions: Object.values(codes) },
16
+ data: { permissions: codes },
17
17
  }
18
18
  : null,
19
19
  config =>
20
20
  fetcher<PermissionCheckResult>(config).then(res => {
21
21
  if (res.has_all) {
22
- return Object.keys(codes).reduce(
22
+ return codes.reduce(
23
23
  (acc, curr) => {
24
24
  acc[curr] = true
25
25
  return acc
@@ -28,9 +28,9 @@ export function usePermissions(codes: Record<string, string>) {
28
28
  )
29
29
  }
30
30
 
31
- return Object.entries(codes).reduce(
31
+ return codes.reduce(
32
32
  (acc, curr) => {
33
- acc[curr[0]] = (res as Record<string, boolean>)[curr[1] as string]
33
+ acc[curr] = (res as Record<string, boolean>)[curr]
34
34
  return acc
35
35
  },
36
36
  {} as Record<string, boolean>,
@@ -42,7 +42,7 @@ export function usePermissions(codes: Record<string, string>) {
42
42
  }
43
43
 
44
44
  export function usePermission(code?: string) {
45
- const { data, isLoading } = usePermissions(code ? { [code]: code } : {})
45
+ const { data, isLoading } = usePermissions(code ? [code] : [])
46
46
 
47
47
  if (!code) {
48
48
  return {
@@ -1,8 +1,7 @@
1
1
  import logo from '@/assets/512_orange_nobackground.png'
2
- import { Layout as AntdLayout, Spin, theme } from 'antd'
3
- import type { FC, ReactNode } from 'react'
4
- import { Suspense } from 'react'
5
- import { Link, Outlet } from 'react-router-dom'
2
+ import { Layout as AntdLayout, theme } from 'antd'
3
+ import type { FC, PropsWithChildren, ReactNode } from 'react'
4
+ import { Link } from 'react-router-dom'
6
5
  import type { ItemType2 } from './NavBar'
7
6
  import NavBar from './NavBar'
8
7
 
@@ -14,8 +13,8 @@ export interface LayoutProps {
14
13
  header?: ReactNode
15
14
  }
16
15
 
17
- const Layout: FC<LayoutProps> = props => {
18
- const { title, items, header } = props
16
+ const Layout: FC<PropsWithChildren<LayoutProps>> = props => {
17
+ const { title, items, header, children } = props
19
18
  const {
20
19
  token: { colorBgContainer, colorBorder },
21
20
  } = theme.useToken()
@@ -38,7 +37,7 @@ const Layout: FC<LayoutProps> = props => {
38
37
  theme="light"
39
38
  >
40
39
  <div className="flex items-end px-6 py-4">
41
- <img src={logo} alt="logo" className="w-8" />
40
+ <img src={logo} alt="logo" className="w-8 h-8" />
42
41
  <Link className="font-bold text-lg ml-2" to="/">
43
42
  {title}
44
43
  </Link>
@@ -57,22 +56,7 @@ const Layout: FC<LayoutProps> = props => {
57
56
  >
58
57
  {header}
59
58
  </Header>
60
- <Content className="p-6 overflow-auto bg-gray-50">
61
- <Suspense
62
- fallback={
63
- <Spin
64
- style={{
65
- display: 'flex',
66
- justifyContent: 'center',
67
- alignItems: 'center',
68
- height: '50vh',
69
- }}
70
- />
71
- }
72
- >
73
- <Outlet />
74
- </Suspense>
75
- </Content>
59
+ <Content className="p-6 overflow-auto bg-gray-50">{children}</Content>
76
60
  </AntdLayout>
77
61
  </AntdLayout>
78
62
  )
@@ -1,7 +1,6 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
1
+ import { usePermissions } from '@/hooks'
2
2
  import { useMenuStore } from '@/stores'
3
- import Icon, * as Icons from '@ant-design/icons'
4
- import { Menu } from 'antd'
3
+ import { Menu, Spin } from 'antd'
5
4
  import type {
6
5
  ItemType,
7
6
  MenuDividerType,
@@ -9,26 +8,24 @@ import type {
9
8
  MenuItemType,
10
9
  SubMenuType,
11
10
  } from 'antd/es/menu/hooks/useItems'
12
- import type { FC, ForwardRefExoticComponent, ReactNode } from 'react'
13
- import { useCallback, useEffect, useMemo, useState } from 'react'
11
+ import type { FC, ReactNode } from 'react'
12
+ import { useCallback, useEffect, useMemo } from 'react'
14
13
  import { Link, useLocation } from 'react-router-dom'
15
14
  import type { Merge } from 'ts-essentials'
16
15
 
17
16
  // 扩展 antd Menu 的类型,使其支持一些我们想要的自定义字段。
18
17
  type MenuItemType2 = Merge<
19
- Omit<MenuItemType, 'icon'>,
18
+ MenuItemType,
20
19
  {
21
20
  code /** 权限编号 **/?: string
22
21
  route /** 前端路由地址 **/?: string
23
- icon?: string
24
22
  }
25
23
  >
26
24
 
27
25
  type SubMenuType2 = Merge<
28
- Omit<SubMenuType, 'icon'>,
26
+ SubMenuType,
29
27
  {
30
28
  children?: ItemType2[]
31
- icon?: string
32
29
  }
33
30
  >
34
31
 
@@ -48,12 +45,7 @@ const withLink = (label?: ReactNode, route?: string): ReactNode => {
48
45
  return label
49
46
  }
50
47
 
51
- /**
52
- * 转换导航配置,主要做了以下几件事情
53
- * 1. 用 Link 元素包装 route
54
- * 2. 收集 code,用于权限判断
55
- */
56
- function transformItems(items: ItemType2[]) {
48
+ function transformItems(items: ItemType2[], permissions?: Record<string, boolean>) {
57
49
  const result: ItemType[] = []
58
50
 
59
51
  for (let i = 0; i < items.length; i++) {
@@ -62,35 +54,20 @@ function transformItems(items: ItemType2[]) {
62
54
  } else if ((items[i] as MenuDividerType).type === 'divider') {
63
55
  result[i] = { ...items[i] } as MenuDividerType
64
56
  } else {
65
- // 引入 icon
66
- const iconName = (items[i] as MenuItemType2 | SubMenuType2).icon
67
-
68
57
  if ((items[i] as SubMenuType2 | MenuItemGroupType2).children) {
69
58
  const { children, ...restProps } = items[i] as SubMenuType2 | MenuItemGroupType2
70
59
  result[i] = {
71
60
  ...restProps,
72
- children: transformItems(children ?? []),
73
- icon: iconName ? <Icon component={(Icons as any)[iconName] as ForwardRefExoticComponent<any>} /> : null,
61
+ children: transformItems(children ?? [], permissions),
74
62
  } as SubMenuType | MenuItemGroupType
75
63
  } else {
76
64
  const { route, label, code, ...restProps } = items[i] as MenuItemType2
77
- // const isPass = code
78
- // ? await httpClient.post<PermissionCheckResult>('/usystem/user/check', { permissions: [code] }).then(res => {
79
- // if (res.has_all) {
80
- // return true
81
- // }
82
- //
83
- // return res[code] ?? false
84
- // })
85
- // : true
86
-
87
- const isPass = true
65
+ const isPass = !code || !permissions || permissions[code]
88
66
 
89
67
  result[i] = isPass
90
68
  ? ({
91
69
  ...restProps,
92
70
  label: withLink(label, route),
93
- icon: iconName ? <Icon component={(Icons as any)[iconName] as ForwardRefExoticComponent<any>} /> : null,
94
71
  } as MenuItemType)
95
72
  : null
96
73
  }
@@ -129,11 +106,9 @@ const NavBar: FC<NavBarProps> = props => {
129
106
  const { items } = props
130
107
  const location = useLocation()
131
108
  const flattenItems = useMemo(() => flatItems(items ?? []), [items])
132
- const [internalItems, setInternalItems] = useState<ItemType<MenuItemType>[]>([])
133
-
134
- useEffect(() => {
135
- setInternalItems(transformItems(items ?? []))
136
- }, [items])
109
+ const codes = flattenItems.map(item => item.code).filter(Boolean) as string[]
110
+ const { data: permissions, isLoading } = usePermissions(codes)
111
+ const internalItems = useMemo(() => transformItems(items ?? [], permissions), [items, permissions])
137
112
 
138
113
  const openKeys = useMenuStore(state => state.openKeys)
139
114
  const selectedKeys = useMenuStore(state => state.selectedKeys)
@@ -161,6 +136,19 @@ const NavBar: FC<NavBarProps> = props => {
161
136
  }
162
137
  }, [flattenItems, location, setOpenKeys, setSelectedKeys])
163
138
 
139
+ if (isLoading) {
140
+ return (
141
+ <Spin
142
+ style={{
143
+ display: 'flex',
144
+ justifyContent: 'center',
145
+ alignItems: 'center',
146
+ height: 'calc(100vh - 64px)',
147
+ }}
148
+ />
149
+ )
150
+ }
151
+
164
152
  return (
165
153
  <Menu
166
154
  style={{ borderRight: 'none' }}