react-toolkits 0.0.7 → 0.1.0

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.
Files changed (39) hide show
  1. package/.turbo/turbo-build.log +8 -6
  2. package/CHANGELOG.md +12 -0
  3. package/README.md +41 -0
  4. package/dist/index.css +252 -1
  5. package/dist/index.css.map +1 -0
  6. package/dist/index.d.mts +55 -54
  7. package/dist/index.esm.js +3576 -15
  8. package/dist/index.esm.js.map +1 -0
  9. package/package.json +2 -1
  10. package/src/components/GameSelect/index.tsx +82 -0
  11. package/src/components/Layout/index.tsx +90 -0
  12. package/src/{layouts/NavBar.tsx → components/NavMenu/index.tsx} +8 -17
  13. package/src/components/ReactToolkitsProvider/context.ts +76 -0
  14. package/src/components/ReactToolkitsProvider/index.tsx +23 -0
  15. package/src/components/UserWidget/index.tsx +46 -0
  16. package/src/components/index.ts +25 -1
  17. package/src/features/permission/components/PermissionCollapse/index.tsx +121 -0
  18. package/src/features/permission/components/PermissionList/index.tsx +17 -118
  19. package/src/features/permission/components/PermissionListV1/index.tsx +42 -0
  20. package/src/features/permission/components/PermissionListV2/index.tsx +146 -0
  21. package/src/features/permission/hooks/index.ts +24 -9
  22. package/src/features/permission/types/index.ts +8 -1
  23. package/src/hooks/use-http-client.tsx +9 -0
  24. package/src/hooks/use-permission.tsx +10 -2
  25. package/src/index.ts +0 -1
  26. package/src/pages/{Login → base/Login}/index.tsx +3 -16
  27. package/src/pages/{NoMatch → base/NotFound}/index.tsx +4 -4
  28. package/src/pages/base/index.tsx +20 -0
  29. package/src/pages/index.ts +3 -4
  30. package/src/pages/permission/RoleList/index.tsx +63 -36
  31. package/src/pages/permission/UserList/index.tsx +2 -2
  32. package/src/pages/permission/index.tsx +26 -1
  33. package/src/stores/index.ts +0 -1
  34. package/src/stores/token.ts +15 -1
  35. package/tsup.config.ts +6 -6
  36. package/src/layouts/Layout.tsx +0 -103
  37. package/src/layouts/index.ts +0 -6
  38. package/src/stores/menu.ts +0 -27
  39. /package/src/pages/{Login → base/Login}/default.tsx +0 -0
@@ -0,0 +1,146 @@
1
+ import { Button, Card, Divider, Empty, Select, Skeleton, Space, Typography } from 'antd'
2
+ import type { FC } from 'react'
3
+ import { useEffect, useState } from 'react'
4
+ import type { RoleV2 } from '../../types'
5
+ import { useAllPermissionsV2 } from '../../hooks'
6
+ import PermissionCollapse from '../PermissionCollapse'
7
+ import type { PermissionListPropsBase } from '../PermissionList'
8
+
9
+ const { Text } = Typography
10
+ const { Option } = Select
11
+
12
+ interface PermissionListV2Props extends PermissionListPropsBase {
13
+ value?: RoleV2['permissions']
14
+ onChange?: (checkedValue: RoleV2['permissions']) => void
15
+ }
16
+
17
+ const PermissionListV2: FC<PermissionListV2Props> = props => {
18
+ const { expand = true, value, readonly, onChange } = props
19
+ const { data: permissions, isLoading, error } = useAllPermissionsV2()
20
+ const [gameList, setGameList] = useState<{ gameId: string; permissions: string[] }[]>([])
21
+
22
+ useEffect(() => {
23
+ const list: { gameId: string; permissions: string[] }[] = []
24
+
25
+ Object.keys(value ?? {}).forEach(key => {
26
+ if (key !== 'global') {
27
+ list.push({ gameId: key, permissions: value?.[key] ?? [] })
28
+ }
29
+ })
30
+
31
+ setGameList(list)
32
+ }, [value])
33
+
34
+ if (error) {
35
+ return (
36
+ <div className="flex justify-center">
37
+ <Text type="danger">权限获取失败</Text>
38
+ </div>
39
+ )
40
+ }
41
+
42
+ const addGame = () => {
43
+ setGameList(prev => [...prev, { gameId: '', permissions: [] }])
44
+ }
45
+
46
+ const removeGame = (index: number) => {
47
+ setGameList(prev => prev.filter((_, i) => i !== index))
48
+ }
49
+
50
+ return (
51
+ <div className="flex flex-col w-full">
52
+ <div className="mb-12">
53
+ <Divider dashed>平台基础权限</Divider>
54
+ </div>
55
+ <Skeleton active loading={isLoading}>
56
+ <PermissionCollapse
57
+ value={value?.global}
58
+ readonly={readonly}
59
+ permissions={permissions?.permission?.slice(0, 2)}
60
+ expand={expand}
61
+ onChange={newValue => {
62
+ onChange?.({
63
+ ...value,
64
+ global: newValue,
65
+ })
66
+ }}
67
+ />
68
+ </Skeleton>
69
+ <div className="my-12">
70
+ <Divider dashed>游戏权限</Divider>
71
+ </div>
72
+ {gameList.map((item, index) => (
73
+ <Card
74
+ title={
75
+ <Space>
76
+ <Text>游戏</Text>
77
+ {readonly ? (
78
+ <Text>{permissions?.game?.find(game => game.id === item.gameId)?.name}</Text>
79
+ ) : (
80
+ <Select
81
+ disabled={readonly}
82
+ value={gameList[index].gameId || undefined}
83
+ style={{ width: '160px' }}
84
+ placeholder="请选择游戏"
85
+ onChange={selectedValue => {
86
+ setGameList(pev => {
87
+ const temp = pev.slice()
88
+ temp[index].gameId = selectedValue
89
+ return temp
90
+ })
91
+ }}
92
+ >
93
+ {permissions?.game?.map(game => (
94
+ <Option key={game.id} value={game.id} disabled={gameList.some(({ gameId }) => gameId === game.id)}>
95
+ {game.name}
96
+ </Option>
97
+ ))}
98
+ </Select>
99
+ )}
100
+ </Space>
101
+ }
102
+ key={index}
103
+ className="mb-6"
104
+ extra={
105
+ !readonly && (
106
+ <Button
107
+ type="link"
108
+ onClick={() => {
109
+ removeGame(index)
110
+ }}
111
+ >
112
+ 移除
113
+ </Button>
114
+ )
115
+ }
116
+ >
117
+ {gameList[index].gameId ? (
118
+ <Skeleton active loading={isLoading}>
119
+ <PermissionCollapse
120
+ value={value?.[gameList[index].gameId]}
121
+ readonly={readonly}
122
+ expand={expand}
123
+ permissions={permissions?.permission?.slice(2)}
124
+ onChange={newValue => {
125
+ onChange?.({
126
+ ...value,
127
+ [gameList[index].gameId]: newValue,
128
+ })
129
+ }}
130
+ />
131
+ </Skeleton>
132
+ ) : (
133
+ <Empty description="请先选择游戏" />
134
+ )}
135
+ </Card>
136
+ ))}
137
+ {!readonly && (
138
+ <Button block type="dashed" onClick={addGame}>
139
+ 添加游戏权限
140
+ </Button>
141
+ )}
142
+ </div>
143
+ )
144
+ }
145
+
146
+ export default PermissionListV2
@@ -1,7 +1,9 @@
1
1
  import { useHttpClient, usePermission } from '@/hooks'
2
2
  import useSWR from 'swr'
3
3
  import useSWRMutation from 'swr/mutation'
4
- import type { PermissionEnumItem, RoleEnumItem } from '../types'
4
+ import type { PermissionEnumItem, RoleEnumItem, RoleV1, RoleV2 } from '../types'
5
+ import type { GameType } from '@/components/GameSelect'
6
+ import { useReactToolkitsContext } from '@/components'
5
7
 
6
8
  export function useAllPermissions() {
7
9
  return useSWR<PermissionEnumItem[]>({
@@ -9,6 +11,15 @@ export function useAllPermissions() {
9
11
  })
10
12
  }
11
13
 
14
+ export function useAllPermissionsV2() {
15
+ return useSWR<{
16
+ game: GameType[]
17
+ permission: PermissionEnumItem[]
18
+ }>({
19
+ url: '/api/usystem/user/allPermissionsV2',
20
+ })
21
+ }
22
+
12
23
  export function useAllRoles() {
13
24
  const { accessible } = usePermission('200005')
14
25
 
@@ -22,23 +33,26 @@ export function useAllRoles() {
22
33
  }
23
34
 
24
35
  export function useRole(name: string) {
25
- return useSWR({
26
- url: '/api/usystem/role/info',
36
+ const isPermissionV2 = useReactToolkitsContext(state => state.isPermissionV2)
37
+
38
+ return useSWR<RoleV1 | RoleV2>({
39
+ url: isPermissionV2 ? '/api/usystem/role/infoV2' : '/api/usystem/role/info',
27
40
  params: { name },
28
41
  })
29
42
  }
30
43
 
31
44
  export function useCreateRole() {
32
45
  const httpClient = useHttpClient()
46
+ const isPermissionV2 = useReactToolkitsContext(state => state.isPermissionV2)
33
47
 
34
48
  return useSWRMutation(
35
- '/api/usystem/role/create',
49
+ isPermissionV2 ? '/api/usystem/role/createV2' : '/api/usystem/role/create',
36
50
  (
37
- url,
51
+ url: string,
38
52
  {
39
53
  arg,
40
54
  }: {
41
- arg: { name: string; permissions: string[] }
55
+ arg: { name: string; permissions: RoleV1['permissions'] | RoleV2['permissions'] }
42
56
  },
43
57
  ) => httpClient.post(url, arg),
44
58
  )
@@ -46,15 +60,16 @@ export function useCreateRole() {
46
60
 
47
61
  export function useUpdateRole() {
48
62
  const httpClient = useHttpClient()
63
+ const isPermissionV2 = useReactToolkitsContext(state => state.isPermissionV2)
49
64
 
50
65
  return useSWRMutation(
51
- '/api/usystem/role/update',
66
+ isPermissionV2 ? '/api/usystem/role/updateV2' : '/api/usystem/role/update',
52
67
  (
53
- url,
68
+ url: string,
54
69
  {
55
70
  arg,
56
71
  }: {
57
- arg: { id: number; name: string; permissions: string[] }
72
+ arg: { id: number; name: string; permissions: RoleV1['permissions'] | RoleV2['permissions'] }
58
73
  },
59
74
  ) => httpClient.post(url, arg),
60
75
  )
@@ -18,13 +18,20 @@ export interface RoleListItem {
18
18
  ctime: string
19
19
  }
20
20
 
21
- export interface Role {
21
+ export interface RoleV1 {
22
22
  id: number
23
23
  name: string
24
24
  ctime: string
25
25
  permissions: string[]
26
26
  }
27
27
 
28
+ export interface RoleV2 {
29
+ id: number
30
+ name: string
31
+ ctime: string
32
+ permissions: Record<string, string[]>
33
+ }
34
+
28
35
  export interface UserListItem {
29
36
  id: string
30
37
  name: string
@@ -2,6 +2,7 @@ import {useTokenStore} from '@/stores'
2
2
  import type {AxiosInstance, AxiosRequestConfig} from 'axios'
3
3
  import axios from 'axios'
4
4
  import type {Merge} from 'ts-essentials'
5
+ import {useReactToolkitsContext} from '@/components'
5
6
 
6
7
  // 覆盖 AxiosInstance 各种请求方法的返回值,为了方便我们在 interceptors.response 里把 AxiosResponse 直接打平成后端返回的数据,去掉了 axios 的封装。
7
8
  type ShimmedAxiosInstance = Merge<
@@ -31,6 +32,7 @@ export class HttpClientError extends Error {
31
32
 
32
33
  export function useHttpClient() {
33
34
  const token = useTokenStore(state => state.token)
35
+ const { game, isGlobalNS, isPermissionV2 } = useReactToolkitsContext(state => state)
34
36
 
35
37
  const defaultOptions: AxiosRequestConfig = {
36
38
  withCredentials: true,
@@ -41,6 +43,13 @@ export function useHttpClient() {
41
43
  instance.interceptors.request.use(config => {
42
44
  const headers = config.headers
43
45
  headers.set('Authorization', `Bearer ${token}`)
46
+
47
+ if (isPermissionV2) {
48
+ if (!headers.has('App-ID')) {
49
+ headers.set('App-ID', isGlobalNS ? 'global' : game?.id)
50
+ }
51
+ }
52
+
44
53
  return config
45
54
  })
46
55
 
@@ -1,21 +1,29 @@
1
1
  import useSWRImmutable from 'swr/immutable'
2
2
  import { useNavigate } from 'react-router-dom'
3
3
  import { useHttpClient } from './use-http-client'
4
+ import { useReactToolkitsContext } from '@/components'
4
5
 
5
6
  export interface PermissionCheckResult {
6
7
  [k: string]: boolean
7
8
  }
8
9
 
9
- export function usePermissions(codes: string[]) {
10
+ export function usePermissions(codes: string[], isGlobalNS = false) {
10
11
  const httpClient = useHttpClient()
11
12
  const navigate = useNavigate()
13
+ const isPermissionV2 = useReactToolkitsContext(state => state.isPermissionV2)
14
+ const url = isPermissionV2 ? '/api/usystem/user/checkV2' : '/api/usystem/user/check'
12
15
 
13
16
  const { data, isLoading } = useSWRImmutable(
14
17
  codes.length > 0
15
18
  ? {
16
19
  method: 'POST',
17
- url: '/api/usystem/user/check',
20
+ url,
18
21
  data: { permissions: codes },
22
+ headers: isGlobalNS
23
+ ? {
24
+ 'App-ID': 'global',
25
+ }
26
+ : {},
19
27
  }
20
28
  : null,
21
29
  config =>
package/src/index.ts CHANGED
@@ -4,5 +4,4 @@ export * from './components'
4
4
  export * from './hooks'
5
5
  export * from './stores'
6
6
  export * from './pages'
7
- export * from './layouts'
8
7
  export * from './constants'
@@ -1,7 +1,7 @@
1
1
  import { SSO_URL } from '@/constants'
2
2
  import { useTokenStore } from '@/stores'
3
3
  import { AliyunOutlined } from '@ant-design/icons'
4
- import { Alert, Button, Card, Col, Divider, Row, Space, Spin, Typography } from 'antd'
4
+ import { Alert, Button, Card, Col, Divider, Row, Space, Typography } from 'antd'
5
5
  import type { FC, PropsWithChildren } from 'react'
6
6
  import { useEffect, useState } from 'react'
7
7
  import { Navigate, useSearchParams } from 'react-router-dom'
@@ -19,7 +19,7 @@ const Login: FC<PropsWithChildren> = props => {
19
19
  const [showAlert, setShowAlert] = useState(false)
20
20
  const httpClient = useHttpClient()
21
21
 
22
- const { isLoading } = useSWRImmutable<{ token: string }>(
22
+ useSWRImmutable<{ token: string }>(
23
23
  searchParams.has('ticket')
24
24
  ? {
25
25
  url: '/api/usystem/user/login',
@@ -30,6 +30,7 @@ const Login: FC<PropsWithChildren> = props => {
30
30
  : null,
31
31
  config => httpClient.request(config),
32
32
  {
33
+ suspense: true,
33
34
  onSuccess: data => {
34
35
  setToken(data.token)
35
36
  },
@@ -44,20 +45,6 @@ const Login: FC<PropsWithChildren> = props => {
44
45
  }
45
46
  }, [searchParams, setSearchParams])
46
47
 
47
- if (isLoading) {
48
- return (
49
- <Spin
50
- style={{
51
- display: 'flex',
52
- justifyContent: 'center',
53
- alignItems: 'center',
54
- width: '100vw',
55
- height: '100vh',
56
- }}
57
- />
58
- )
59
- }
60
-
61
48
  if (token) {
62
49
  return <Navigate replace to="/" />
63
50
  }
@@ -1,14 +1,14 @@
1
1
  import { Button, Result } from 'antd'
2
2
  import { useNavigate } from 'react-router-dom'
3
3
 
4
- const NoMatch = () => {
4
+ const NotFound = () => {
5
5
  const navigate = useNavigate()
6
6
  return (
7
7
  <div className="h-screen flex justify-center items-center">
8
8
  <Result
9
9
  status="404"
10
10
  title="404"
11
- subTitle="Sorry, the page you visited does not exist."
11
+ subTitle="访问的页面不存在"
12
12
  extra={
13
13
  <Button
14
14
  type="primary"
@@ -16,7 +16,7 @@ const NoMatch = () => {
16
16
  navigate('/')
17
17
  }}
18
18
  >
19
- Back Home
19
+ 返回页面
20
20
  </Button>
21
21
  }
22
22
  />
@@ -24,4 +24,4 @@ const NoMatch = () => {
24
24
  )
25
25
  }
26
26
 
27
- export default NoMatch
27
+ export default NotFound
@@ -0,0 +1,20 @@
1
+ import { lazy } from 'react'
2
+ import type { RouteObject } from 'react-router-dom'
3
+
4
+ const Login = lazy(() => import('./Login'))
5
+ const NotFound = lazy(() => import('./NotFound'))
6
+
7
+ const routes: RouteObject = {
8
+ children: [
9
+ {
10
+ path: 'login',
11
+ element: <Login />,
12
+ },
13
+ {
14
+ path: '*',
15
+ element: <NotFound />,
16
+ },
17
+ ],
18
+ }
19
+
20
+ export default routes
@@ -1,5 +1,4 @@
1
- import Login from './Login'
2
- import NoMatch from './NoMatch'
3
- import permission from './permission'
1
+ import baseRoutes from './base'
2
+ import permissionRoutes from './permission'
4
3
 
5
- export { Login, NoMatch, permission }
4
+ export { baseRoutes, permissionRoutes }
@@ -1,13 +1,13 @@
1
- import { Highlight, PermissionButton, QueryList } from '@/components'
1
+ import { Highlight, PermissionButton, QueryList, useReactToolkitsContext } from '@/components'
2
2
  import { useFormModal } from '@/components/FormModal/hooks'
3
- import type { Role, RoleListItem } from '@/features/permission'
3
+ import type { RoleListItem, RoleV1, RoleV2 } from '@/features/permission'
4
4
  import { PermissionList, useCreateRole, useRemoveRole, useUpdateRole } from '@/features/permission'
5
5
  import { useHttpClient, usePermission } from '@/hooks'
6
6
  import { useQueryListStore } from '@/stores'
7
7
  import { UsergroupAddOutlined } from '@ant-design/icons'
8
8
  import type { TableColumnsType } from 'antd'
9
9
  import { App, Card, Form, Input, Space } from 'antd'
10
- import { useMemo } from 'react'
10
+ import { useCallback, useMemo } from 'react'
11
11
  import { Link } from 'react-router-dom'
12
12
  import type { ListResponse } from '@/types'
13
13
 
@@ -15,18 +15,32 @@ export const swrKey = {
15
15
  url: '/api/usystem/role/list',
16
16
  }
17
17
 
18
- const RoleList = () => {
19
- const { accessible: viewable } = usePermission('200005')
20
- const { modal, message } = App.useApp()
21
- const httpClient = useHttpClient()
22
- const create = useCreateRole()
23
- const remove = useRemoveRole()
24
- const update = useUpdateRole()
18
+ const useCreateModal = () => {
19
+ const { message } = App.useApp()
25
20
  const refresh = useQueryListStore(state => state.refresh)
21
+ const create = useCreateRole()
26
22
 
27
- const { showModal: showCreateModal, Modal: CreateModal } = useFormModal<{
23
+ const onConfirm = useCallback(
24
+ async (values: { name: string; permissions: RoleV1['permissions'] | RoleV2['permissions'] }) => {
25
+ await create.trigger(
26
+ {
27
+ name: `role_${values.name}`,
28
+ permissions: values.permissions,
29
+ },
30
+ {
31
+ async onSuccess() {
32
+ await message.success('角色创建成功')
33
+ refresh(swrKey, { page: 1 })
34
+ },
35
+ },
36
+ )
37
+ },
38
+ [create, refresh, message],
39
+ )
40
+
41
+ return useFormModal<{
28
42
  name: string
29
- permissions: string[]
43
+ permissions: RoleV1['permissions'] | RoleV2['permissions']
30
44
  }>({
31
45
  title: '创建角色',
32
46
  width: '50vw',
@@ -41,26 +55,38 @@ const RoleList = () => {
41
55
  </Form.Item>
42
56
  </>
43
57
  ),
44
- async onConfirm(values) {
45
- await create.trigger(
58
+ onConfirm,
59
+ })
60
+ }
61
+
62
+ const useUpdateModal = () => {
63
+ const { message } = App.useApp()
64
+ const refresh = useQueryListStore(state => state.refresh)
65
+ const update = useUpdateRole()
66
+
67
+ const onConfirm = useCallback(
68
+ async (values: { id: number; name: string; permissions: RoleV1['permissions'] | RoleV2['permissions'] }) => {
69
+ await update.trigger(
46
70
  {
71
+ id: values.id,
47
72
  name: `role_${values.name}`,
48
73
  permissions: values.permissions,
49
74
  },
50
75
  {
51
76
  async onSuccess() {
52
- await message.success('角色创建成功')
77
+ await message.success('角色更新成功')
53
78
  refresh(swrKey, { page: 1 })
54
79
  },
55
80
  },
56
81
  )
57
82
  },
58
- })
83
+ [update, refresh, message],
84
+ )
59
85
 
60
- const { showModal: showUpdateModal, Modal: UpdateModal } = useFormModal<{
86
+ return useFormModal<{
61
87
  id: number
62
88
  name: string
63
- permissions: string[]
89
+ permissions: RoleV1['permissions'] | RoleV2['permissions']
64
90
  }>({
65
91
  title: '更新角色',
66
92
  width: '50vw',
@@ -78,22 +104,19 @@ const RoleList = () => {
78
104
  </Form.Item>
79
105
  </>
80
106
  ),
81
- async onConfirm(values) {
82
- await update.trigger(
83
- {
84
- id: values.id,
85
- name: `role_${values.name}`,
86
- permissions: values.permissions,
87
- },
88
- {
89
- async onSuccess() {
90
- await message.success('角色更新成功')
91
- refresh(swrKey, { page: 1 })
92
- },
93
- },
94
- )
95
- },
107
+ onConfirm,
96
108
  })
109
+ }
110
+
111
+ const RoleList = () => {
112
+ const { accessible: viewable } = usePermission('200005')
113
+ const { modal, message } = App.useApp()
114
+ const httpClient = useHttpClient()
115
+ const isPermissionV2 = useReactToolkitsContext(state => state.isPermissionV2)
116
+ const remove = useRemoveRole()
117
+ const refresh = useQueryListStore(state => state.refresh)
118
+ const { showModal: showCreateModal, Modal: CreateModal } = useCreateModal()
119
+ const { showModal: showUpdateModal, Modal: UpdateModal } = useUpdateModal()
97
120
 
98
121
  const columns = useMemo<TableColumnsType<RoleListItem>>(
99
122
  () => [
@@ -130,9 +153,13 @@ const RoleList = () => {
130
153
  size="small"
131
154
  type="link"
132
155
  onClick={async () => {
133
- const role = await httpClient.get<Role>('/api/usystem/role/info', {
134
- params: { name: value.name },
135
- })
156
+ const role = await httpClient.get<RoleV1 | RoleV2>(
157
+ isPermissionV2 ? '/api/usystem/role/infoV2' : '/api/usystem/role/info',
158
+ {
159
+ params: { name: value.name },
160
+ },
161
+ )
162
+
136
163
  showUpdateModal({
137
164
  initialValues: {
138
165
  id: role?.id,
@@ -23,7 +23,7 @@ function useCreatingUserModal() {
23
23
  const refresh = useQueryListStore(state => state.refresh)
24
24
 
25
25
  return useFormModal<{ id: string; name: string; roles: string[] }>({
26
- title: '创建角色',
26
+ title: '创建用户',
27
27
  labelCol: { flex: '80px' },
28
28
  content: (
29
29
  <>
@@ -63,7 +63,7 @@ function useUpdatingUserModal() {
63
63
  const refresh = useQueryListStore(state => state.refresh)
64
64
 
65
65
  return useFormModal<{ id: string; name: string; roles: string[] }>({
66
- title: '更新角色',
66
+ title: '更新用户',
67
67
  labelCol: { flex: '80px' },
68
68
  content: (
69
69
  <>
@@ -1,13 +1,38 @@
1
1
  import { lazy } from 'react'
2
2
  import type { RouteObject } from 'react-router-dom'
3
- import { Navigate } from 'react-router-dom'
3
+ import { Navigate, Outlet } from 'react-router-dom'
4
+ import { Layout, ReactToolkitsProvider, useReactToolkitsContext } from '@/components'
5
+ import { SWRConfig } from 'swr'
6
+ import { useHttpClient } from '@/hooks'
4
7
 
5
8
  const UserList = lazy(() => import('./UserList'))
6
9
  const RoleList = lazy(() => import('./RoleList'))
7
10
  const RoleDetail = lazy(() => import('./RoleDetail'))
8
11
 
12
+ const PermissionRoot = () => {
13
+ const httpClient = useHttpClient()
14
+ const configs = useReactToolkitsContext(state => state)
15
+
16
+ return (
17
+ // NOTE: 目前嵌套的 ReactToolkitsProvider 只能手动注入父级 ReactToolkitsProvider 的配置
18
+ <ReactToolkitsProvider {...configs} isGlobalNS>
19
+ <SWRConfig
20
+ value={{
21
+ fetcher: httpClient.request,
22
+ shouldRetryOnError: false,
23
+ }}
24
+ >
25
+ <Layout>
26
+ <Outlet />
27
+ </Layout>
28
+ </SWRConfig>
29
+ </ReactToolkitsProvider>
30
+ )
31
+ }
32
+
9
33
  const routes: RouteObject = {
10
34
  path: 'permission',
35
+ element: <PermissionRoot />,
11
36
  children: [
12
37
  {
13
38
  index: true,
@@ -1,3 +1,2 @@
1
- export * from './menu'
2
1
  export * from './token'
3
2
  export * from './query-list'
@@ -1,16 +1,30 @@
1
1
  import { create } from 'zustand'
2
2
  import { persist } from 'zustand/middleware'
3
+ import jwtDecode from 'jwt-decode'
4
+
5
+ interface UserInfo {
6
+ authorityId: string
7
+ exp: number
8
+ }
3
9
 
4
10
  export interface TokenState {
5
11
  token: string
12
+ getUser: () => UserInfo | null
6
13
  setToken: (token: string) => void
7
14
  clearToken: () => void
8
15
  }
9
16
 
10
17
  export const useTokenStore = create<TokenState>()(
11
18
  persist(
12
- set => ({
19
+ (set, get) => ({
13
20
  token: '',
21
+ getUser: () => {
22
+ try {
23
+ return jwtDecode(get().token) as UserInfo
24
+ } catch (_) {
25
+ return null
26
+ }
27
+ },
14
28
  setToken: token => set({ token }),
15
29
  clearToken: () => {
16
30
  set({ token: '' })