react-toolkits 0.0.8 → 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 (38) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +41 -0
  4. package/dist/index.css +251 -1
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.d.mts +55 -54
  7. package/dist/index.esm.js +3574 -15
  8. package/dist/index.esm.js.map +1 -1
  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/{NoMatch → base/NotFound}/index.tsx +4 -4
  27. package/src/pages/base/index.tsx +20 -0
  28. package/src/pages/index.ts +3 -4
  29. package/src/pages/permission/RoleList/index.tsx +63 -36
  30. package/src/pages/permission/index.tsx +26 -1
  31. package/src/stores/index.ts +0 -1
  32. package/src/stores/token.ts +15 -1
  33. package/tsup.config.ts +1 -1
  34. package/src/layouts/Layout.tsx +0 -103
  35. package/src/layouts/index.ts +0 -6
  36. package/src/stores/menu.ts +0 -27
  37. /package/src/pages/{Login → base/Login}/default.tsx +0 -0
  38. /package/src/pages/{Login → base/Login}/index.tsx +0 -0
@@ -1,129 +1,28 @@
1
- import { Checkbox, Collapse, Skeleton, Typography } from 'antd'
2
- import type { CheckboxChangeEvent } from 'antd/es/checkbox'
3
- import type { CheckboxValueType } from 'antd/es/checkbox/Group'
4
- import { useEffect, useState } from 'react'
5
- import { useAllPermissions } from '../../hooks'
1
+ import type { RoleV1, RoleV2 } from '@/features/permission'
2
+ import { useReactToolkitsContext } from '@/components'
3
+ import type { FC } from 'react'
4
+ import PermissionListV1 from '../PermissionListV1'
5
+ import PermissionListV2 from '../PermissionListV2'
6
6
 
7
- const { Text } = Typography
8
-
9
- const PermissionList = ({
10
- expand = true,
11
- value,
12
- readonly,
13
- onChange,
14
- }: {
7
+ export interface PermissionListPropsBase {
15
8
  expand?: boolean
16
- value?: CheckboxValueType[]
17
9
  readonly?: boolean
18
- onChange?: (checkedValue: CheckboxValueType[]) => void
19
- }) => {
20
- const [activeKey, setActiveKey] = useState<string[]>([])
21
- const [internalValue, setInternalValue] = useState<CheckboxValueType[]>(value ?? [])
22
- const { data: permissions, isLoading, error } = useAllPermissions()
23
-
24
- const [checkedMap, setCheckedMap] = useState<Record<string, boolean>>({})
25
-
26
- useEffect(() => {
27
- setInternalValue(value ?? [])
28
- }, [value])
29
-
30
- useEffect(() => {
31
- if (expand) {
32
- setActiveKey((permissions ?? []).map(({ category }) => category))
33
- }
34
- }, [expand, permissions])
35
-
36
- useEffect(() => {
37
- const checkedValue = (permissions ?? []).reduce(
38
- (acc, curr) => {
39
- acc[curr.category] = curr.permissions.every(item => internalValue.includes(item.value))
40
- return acc
41
- },
42
- {} as Record<string, boolean>,
43
- )
44
-
45
- setCheckedMap(checkedValue)
46
- }, [internalValue, permissions])
47
-
48
- const onCollapseChange = (key: string | string[]) => {
49
- setActiveKey(key as string[])
50
- }
51
-
52
- const getCheckedValue = (checkedValue: boolean, codes: string[]) => {
53
- let tempValue: CheckboxValueType[] = []
54
-
55
- if (checkedValue) {
56
- tempValue = [...new Set(internalValue.concat(codes))]
57
- } else {
58
- tempValue = internalValue.slice()
59
-
60
- codes.forEach(code => {
61
- const index = tempValue.findIndex(item => item === code)
62
- if (index > -1) {
63
- tempValue.splice(index, 1)
64
- }
65
- })
66
- }
10
+ }
67
11
 
68
- return tempValue
69
- }
12
+ interface PermissionListProps extends PermissionListPropsBase {
13
+ value?: RoleV1['permissions'] | RoleV2['permissions']
14
+ onChange?: (checkedValue: RoleV1['permissions'] | RoleV2['permissions']) => void
15
+ }
70
16
 
71
- const onCheckChange = (e: CheckboxChangeEvent, category: string, codes: string[]) => {
72
- const checkedValue = getCheckedValue(e.target.checked, codes)
73
- setInternalValue(checkedValue)
74
- onChange?.(checkedValue)
75
- }
17
+ const PermissionList: FC<PermissionListProps> = (props: PermissionListProps) => {
18
+ const { value } = props
19
+ const isPermissionV2 = useReactToolkitsContext(state => state.isPermissionV2)
76
20
 
77
- if (error) {
78
- return (
79
- <div className="flex justify-center">
80
- <Text type="danger">权限获取失败</Text>
81
- </div>
82
- )
21
+ if (isPermissionV2) {
22
+ return <PermissionListV2 {...props} value={value as RoleV2['permissions']} />
83
23
  }
84
24
 
85
- return (
86
- <Skeleton active loading={isLoading}>
87
- <Collapse
88
- style={{ width: '100%' }}
89
- collapsible="header"
90
- activeKey={activeKey}
91
- items={(permissions ?? []).map(item => ({
92
- key: item.category,
93
- label: item.category,
94
- extra: !readonly && (
95
- <Checkbox
96
- checked={checkedMap[item.category]}
97
- onChange={e => {
98
- onCheckChange(
99
- e,
100
- item.category,
101
- item.permissions.map(permission => permission.value),
102
- )
103
- }}
104
- >
105
- 全选
106
- </Checkbox>
107
- ),
108
- children: (
109
- <Checkbox.Group
110
- style={{ width: '100%' }}
111
- options={item.permissions.map(permission => ({
112
- label: permission.label,
113
- value: permission.value,
114
- disabled: readonly,
115
- onChange(e) {
116
- onCheckChange(e, item.category, [permission.value])
117
- },
118
- }))}
119
- value={internalValue}
120
- />
121
- ),
122
- }))}
123
- onChange={onCollapseChange}
124
- />
125
- </Skeleton>
126
- )
25
+ return <PermissionListV1 {...props} value={value as RoleV1['permissions']} />
127
26
  }
128
27
 
129
28
  export default PermissionList
@@ -0,0 +1,42 @@
1
+ import { Skeleton, Typography } from 'antd'
2
+ import { useAllPermissions } from '../../hooks'
3
+ import PermissionCollapse from '../PermissionCollapse'
4
+ import type { RoleV1 } from '../../types'
5
+ import type { PermissionListPropsBase } from '../PermissionList'
6
+ import type { FC } from 'react'
7
+
8
+ const { Text } = Typography
9
+
10
+ interface PermissionListV1Props extends PermissionListPropsBase {
11
+ value?: RoleV1['permissions']
12
+ onChange?: (checkedValue: RoleV1['permissions']) => void
13
+ }
14
+
15
+ const PermissionListV1: FC<PermissionListV1Props> = props => {
16
+ const { expand = true, value, readonly, onChange } = props
17
+ const { data: permissions, isLoading, error } = useAllPermissions()
18
+
19
+ if (error) {
20
+ return (
21
+ <div className="flex justify-center">
22
+ <Text type="danger">权限获取失败</Text>
23
+ </div>
24
+ )
25
+ }
26
+
27
+ return (
28
+ <Skeleton active loading={isLoading}>
29
+ <PermissionCollapse
30
+ value={value}
31
+ permissions={permissions}
32
+ readonly={readonly}
33
+ expand={expand}
34
+ onChange={newValue => {
35
+ onChange?.(newValue)
36
+ }}
37
+ />
38
+ </Skeleton>
39
+ )
40
+ }
41
+
42
+ export default PermissionListV1
@@ -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,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 }