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,90 @@
1
+ import logo from '@/assets/512_orange_nobackground.png'
2
+ import * as Antd from 'antd'
3
+ import { Divider, Space } from 'antd'
4
+ import type { FC, PropsWithChildren } from 'react'
5
+ import * as React from 'react'
6
+ import { Suspense } from 'react'
7
+ import { Link } from 'react-router-dom'
8
+ import { GameSelect, NavMenu, useReactToolkitsContext, UserWidget } from '@/components'
9
+
10
+ const { Spin, theme } = Antd
11
+ const { Header, Sider, Content } = Antd.Layout
12
+
13
+ export interface LayoutProps extends PropsWithChildren {
14
+ extra?: React.ReactNode
15
+ }
16
+
17
+ const Layout: FC<LayoutProps> = props => {
18
+ const { children, extra } = props
19
+ const {
20
+ token: { colorBgContainer, colorBorder },
21
+ } = theme.useToken()
22
+ const title = useReactToolkitsContext(state => state.title)
23
+
24
+ return (
25
+ <Antd.Layout hasSider className="h-screen">
26
+ <Sider
27
+ width={256}
28
+ style={{
29
+ overflow: 'auto',
30
+ height: '100vh',
31
+ position: 'fixed',
32
+ left: 0,
33
+ top: 0,
34
+ bottom: 0,
35
+ borderRightWidth: 1,
36
+ borderRightStyle: 'solid',
37
+ borderRightColor: colorBorder,
38
+ }}
39
+ theme="light"
40
+ >
41
+ <div className="flex items-end px-6 py-4">
42
+ <img src={logo} alt="logo" className="w-8 h-8" />
43
+ <Link className="font-bold text-lg ml-2" to="/">
44
+ {title}
45
+ </Link>
46
+ </div>
47
+ <NavMenu />
48
+ </Sider>
49
+ <Antd.Layout className="ml-64">
50
+ <Header
51
+ style={{
52
+ padding: '0 24px',
53
+ background: colorBgContainer,
54
+ borderBottomWidth: 1,
55
+ borderBottomStyle: 'solid',
56
+ borderBottomColor: colorBorder,
57
+ }}
58
+ >
59
+ <div className="flex justify-between items-center h-full">
60
+ <div>
61
+ <GameSelect />
62
+ </div>
63
+ <Space size="small" split={<Divider type="vertical" />}>
64
+ {extra}
65
+ <UserWidget />
66
+ </Space>
67
+ </div>
68
+ </Header>
69
+ <Content className="p-6 overflow-auto bg-gray-50">
70
+ <Suspense
71
+ fallback={
72
+ <Spin
73
+ style={{
74
+ display: 'flex',
75
+ justifyContent: 'center',
76
+ alignItems: 'center',
77
+ height: '50vh',
78
+ }}
79
+ />
80
+ }
81
+ >
82
+ {children}
83
+ </Suspense>
84
+ </Content>
85
+ </Antd.Layout>
86
+ </Antd.Layout>
87
+ )
88
+ }
89
+
90
+ export default Layout
@@ -1,5 +1,4 @@
1
1
  import {usePermissions} from '@/hooks'
2
- import {useMenuStore} from '@/stores'
3
2
  import {Menu} from 'antd'
4
3
  import type {
5
4
  ItemType,
@@ -8,10 +7,11 @@ import type {
8
7
  MenuItemType,
9
8
  SubMenuType,
10
9
  } from 'antd/es/menu/hooks/useItems'
11
- import type {FC, ReactNode} from 'react'
10
+ import type {ReactNode} from 'react'
12
11
  import {useCallback, useEffect, useMemo} from 'react'
13
12
  import {Link, useLocation} from 'react-router-dom'
14
13
  import type {Merge} from 'ts-essentials'
14
+ import {useReactToolkitsContext} from '@/components'
15
15
 
16
16
  // 扩展 antd Menu 的类型,使其支持一些我们想要的自定义字段。
17
17
  type MenuItemType2 = Merge<
@@ -98,29 +98,20 @@ function flatItems(
98
98
  return result
99
99
  }
100
100
 
101
- export interface NavBarProps {
102
- items: ItemType2[]
103
- }
104
-
105
- const NavBar: FC<NavBarProps> = props => {
106
- const { items } = props
101
+ const NavMenu = () => {
107
102
  const location = useLocation()
103
+ const items = useReactToolkitsContext(state => state.menuItems)
108
104
  const flattenItems = useMemo(() => flatItems(items ?? []), [items])
109
105
  const codes = flattenItems.map(item => item.code).filter(Boolean) as string[]
110
- const { data: permissions } = usePermissions(codes)
106
+ const { data: permissions } = usePermissions(codes, true)
111
107
  const internalItems = useMemo(() => transformItems(items ?? [], permissions), [items, permissions])
112
-
113
- const openKeys = useMenuStore(state => state.openKeys)
114
- const selectedKeys = useMenuStore(state => state.selectedKeys)
115
- const setOpenKeys = useMenuStore(state => state.setOpenKeys)
116
- const setSelectedKeys = useMenuStore(state => state.setSelectedKeys)
108
+ const { openKeys, selectedKeys, setOpenKeys, setSelectedKeys } = useReactToolkitsContext(state => state)
117
109
 
118
110
  const onOpenChange = useCallback(
119
111
  (keys: string[]) => {
120
112
  const latestOpenKey = keys?.find(key => openKeys?.indexOf(key) === -1)
121
113
  const match = flattenItems.find(item => latestOpenKey === item.key)
122
- const _openKeys = (match?.keypath ?? [latestOpenKey]) as string[]
123
- setOpenKeys(_openKeys)
114
+ setOpenKeys((match?.keypath ?? [latestOpenKey]) as string[])
124
115
  },
125
116
  [flattenItems, openKeys, setOpenKeys],
126
117
  )
@@ -148,4 +139,4 @@ const NavBar: FC<NavBarProps> = props => {
148
139
  )
149
140
  }
150
141
 
151
- export default NavBar
142
+ export default NavMenu
@@ -0,0 +1,76 @@
1
+ import {create, useStore} from 'zustand'
2
+ import {createContext, useContext} from 'react'
3
+ import type {StateStorage} from 'zustand/middleware'
4
+ import {createJSONStorage, persist} from 'zustand/middleware'
5
+ import type {GameType} from '../GameSelect'
6
+ import type {ItemType2} from '../NavMenu'
7
+
8
+ // SessionStorage 在同一域下的不同页面间是隔离的,用于防止多开页面时的数据冲突
9
+ const mixedStorage: StateStorage = {
10
+ getItem: (name: string): string | null => {
11
+ return sessionStorage.getItem(name) || localStorage.getItem(name)
12
+ },
13
+ setItem: (name: string, value: string) => {
14
+ localStorage.setItem(name, value)
15
+ sessionStorage.setItem(name, value)
16
+ },
17
+ removeItem: async (name: string) => {
18
+ localStorage.removeItem(name)
19
+ sessionStorage.removeItem(name)
20
+ },
21
+ }
22
+
23
+ export interface ReactToolkitsState {
24
+ title: string
25
+ isPermissionV2: boolean
26
+ isGlobalNS: boolean
27
+ game: GameType | null
28
+ setGame: (game: GameType | null) => void
29
+ openKeys: string[]
30
+ selectedKeys: string[]
31
+ setOpenKeys: (keys: string[]) => void
32
+ setSelectedKeys: (keys: string[]) => void
33
+ menuItems: ItemType2[]
34
+ }
35
+
36
+ export type ReactToolkitsStore = ReturnType<typeof createReactToolkitsStore>
37
+
38
+ export const createReactToolkitsStore = () => {
39
+ return create<ReactToolkitsState>()(
40
+ persist(
41
+ set => ({
42
+ title: '',
43
+ isPermissionV2: false,
44
+ isGlobalNS: false,
45
+ game: null,
46
+ setGame: game => set({ game }),
47
+ openKeys: [],
48
+ setOpenKeys: keys => set({ openKeys: keys }),
49
+ selectedKeys: [],
50
+ setSelectedKeys: keys => set({ selectedKeys: keys }),
51
+ menuItems: [],
52
+ }),
53
+ {
54
+ name: 'ReactToolkits',
55
+ storage: createJSONStorage(() => mixedStorage),
56
+ partialize: state => ({
57
+ title: state.title,
58
+ game: state.game,
59
+ openKeys: state.openKeys,
60
+ selectedKeys: state.selectedKeys,
61
+ }),
62
+ },
63
+ ),
64
+ )
65
+ }
66
+
67
+ export const ReactToolkitsContext = createContext<ReactToolkitsStore | null>(null)
68
+
69
+ export function useReactToolkitsContext<T>(
70
+ selector: (state: ReactToolkitsState) => T,
71
+ equalityFn?: (left: T, right: T) => boolean,
72
+ ): T {
73
+ const store = useContext(ReactToolkitsContext)
74
+ if (!store) throw new Error('Missing ReactToolkitsContext.Provider in the tree')
75
+ return useStore(store, selector, equalityFn)
76
+ }
@@ -0,0 +1,23 @@
1
+ import type { FC, PropsWithChildren } from 'react'
2
+ import { useEffect, useRef } from 'react'
3
+ import type { ReactToolkitsState, ReactToolkitsStore } from './context'
4
+ import { createReactToolkitsStore, ReactToolkitsContext } from './context'
5
+
6
+ const ReactToolkitsProvider: FC<
7
+ PropsWithChildren<Partial<Pick<ReactToolkitsState, 'isPermissionV2' | 'isGlobalNS' | 'menuItems' | 'title'>>>
8
+ > = props => {
9
+ const { children, ...restProps } = props
10
+ const storeRef = useRef<ReactToolkitsStore>()
11
+
12
+ if (!storeRef.current) {
13
+ storeRef.current = createReactToolkitsStore()
14
+ }
15
+
16
+ useEffect(() => {
17
+ storeRef.current?.setState(restProps)
18
+ }, [restProps])
19
+
20
+ return <ReactToolkitsContext.Provider value={storeRef.current}>{children}</ReactToolkitsContext.Provider>
21
+ }
22
+
23
+ export default ReactToolkitsProvider
@@ -0,0 +1,46 @@
1
+ import type { FC } from 'react'
2
+ import { useNavigate } from 'react-router-dom'
3
+ import { Dropdown, Space } from 'antd'
4
+ import Link from 'antd/es/typography/Link'
5
+ import { LogoutOutlined, UserOutlined } from '@ant-design/icons'
6
+ import { useTokenStore } from '@/stores'
7
+
8
+ const UserWidget: FC = props => {
9
+ const navigate = useNavigate()
10
+ const clearToken = useTokenStore(state => state.clearToken)
11
+ const user = useTokenStore(state => state.getUser())
12
+
13
+ return (
14
+ <Dropdown
15
+ menu={{
16
+ selectable: true,
17
+ items: [
18
+ {
19
+ key: '1',
20
+ label: (
21
+ <Link
22
+ onClick={() => {
23
+ clearToken()
24
+ navigate('/login')
25
+ }}
26
+ >
27
+ 登出
28
+ </Link>
29
+ ),
30
+ icon: <LogoutOutlined />,
31
+ },
32
+ ],
33
+ }}
34
+ placement="bottomRight"
35
+ >
36
+ <Link>
37
+ <Space align="center">
38
+ <span>{user?.authorityId}</span>
39
+ <UserOutlined style={{ fontSize: '16px' }} />
40
+ </Space>
41
+ </Link>
42
+ </Dropdown>
43
+ )
44
+ }
45
+
46
+ export default UserWidget
@@ -12,8 +12,30 @@ import type { PermissionButtonProps } from './PermissionButton'
12
12
  import PermissionButton from './PermissionButton'
13
13
  import type { QueryListKey, QueryListProps } from './QueryList'
14
14
  import QueryList from './QueryList'
15
+ import { useReactToolkitsContext } from './ReactToolkitsProvider/context'
16
+ import ReactToolkitsProvider from './ReactToolkitsProvider'
17
+ import GameSelect from './GameSelect'
18
+ import UserWidget from './UserWidget'
19
+ import type { ItemType2 } from './NavMenu'
20
+ import NavMenu from './NavMenu'
21
+ import type { LayoutProps } from './Layout'
22
+ import Layout from './Layout'
15
23
 
16
- export { FormModal, PermissionButton, DynamicTags, QueryList, FilterForm, Highlight, useFormModal }
24
+ export {
25
+ FormModal,
26
+ PermissionButton,
27
+ DynamicTags,
28
+ QueryList,
29
+ FilterForm,
30
+ Highlight,
31
+ useFormModal,
32
+ useReactToolkitsContext,
33
+ ReactToolkitsProvider,
34
+ GameSelect,
35
+ UserWidget,
36
+ NavMenu,
37
+ Layout,
38
+ }
17
39
  export type {
18
40
  DynamicTagsProps,
19
41
  FilterFormProps,
@@ -24,4 +46,6 @@ export type {
24
46
  QueryListKey,
25
47
  HighlightTextsProps,
26
48
  PermissionButtonProps,
49
+ ItemType2,
50
+ LayoutProps,
27
51
  }
@@ -0,0 +1,121 @@
1
+ import type { PermissionEnumItem } from '@/features/permission'
2
+ import type { FC } from 'react'
3
+ import { useCallback, useEffect, useState } from 'react'
4
+ import type { CheckboxChangeEvent } from 'antd/es/checkbox'
5
+ import { Checkbox, Col, Collapse, Row } from 'antd'
6
+
7
+ const { Panel } = Collapse
8
+
9
+ interface PermissionCollapseProps {
10
+ expand?: boolean
11
+ permissions?: PermissionEnumItem[]
12
+ readonly?: boolean
13
+ value?: string[]
14
+ onChange?: (value: string[]) => void
15
+ }
16
+
17
+ const PermissionCollapse: FC<PermissionCollapseProps> = props => {
18
+ const { permissions, readonly, expand, value, onChange } = props
19
+ const [activeKey, setActiveKey] = useState<string[]>([])
20
+ const [checkedMap, setCheckedMap] = useState<Record<string, boolean>>({})
21
+ const [internalValue, setInternalValue] = useState<string[]>(value ?? [])
22
+
23
+ const onCollapseChange = useCallback((key: string | string[]) => {
24
+ setActiveKey(key as string[])
25
+ }, [])
26
+
27
+ const getCheckedValue = (checkedValue: boolean, codes: string[]) => {
28
+ let tempValue: string[] = []
29
+
30
+ if (checkedValue) {
31
+ tempValue = [...new Set(internalValue.concat(codes))]
32
+ } else {
33
+ tempValue = internalValue.slice()
34
+
35
+ codes.forEach(code => {
36
+ const index = tempValue.findIndex(item => item === code)
37
+ if (index > -1) {
38
+ tempValue.splice(index, 1)
39
+ }
40
+ })
41
+ }
42
+
43
+ return tempValue
44
+ }
45
+
46
+ const onCheckChange = (e: CheckboxChangeEvent, category: string, codes: string[]) => {
47
+ const checkedValue = getCheckedValue(e.target.checked, codes)
48
+ setInternalValue(checkedValue)
49
+ onChange?.(checkedValue)
50
+ }
51
+
52
+ useEffect(() => {
53
+ setInternalValue(value ?? [])
54
+ }, [value])
55
+
56
+ useEffect(() => {
57
+ if (expand) {
58
+ setActiveKey((permissions ?? []).map(({ category }) => category))
59
+ }
60
+ }, [expand, permissions])
61
+
62
+ useEffect(() => {
63
+ const checkedValue = (permissions ?? []).reduce(
64
+ (acc, curr) => {
65
+ acc[curr.category] = curr.permissions.every(item => internalValue.includes(item.value))
66
+ return acc
67
+ },
68
+ {} as Record<string, boolean>,
69
+ )
70
+
71
+ setCheckedMap(checkedValue)
72
+ }, [internalValue, permissions])
73
+
74
+ return (
75
+ <Collapse
76
+ style={{ width: '100%' }}
77
+ collapsible="header"
78
+ activeKey={activeKey}
79
+ items={(permissions ?? []).map(item => ({
80
+ key: item.category,
81
+ label: item.category,
82
+ extra: !readonly && (
83
+ <Checkbox
84
+ checked={checkedMap[item.category]}
85
+ onChange={e => {
86
+ onCheckChange(
87
+ e,
88
+ item.category,
89
+ item.permissions.map(permission => permission.value),
90
+ )
91
+ }}
92
+ >
93
+ 全选
94
+ </Checkbox>
95
+ ),
96
+ children: (
97
+ <Checkbox.Group style={{ width: '100%' }} value={internalValue}>
98
+ <Row gutter={[10, 10]} style={{ width: '100%' }}>
99
+ {item.permissions.map(permission => (
100
+ <Col key={permission.value} span={6}>
101
+ <Checkbox
102
+ disabled={readonly}
103
+ value={permission.value}
104
+ onChange={e => {
105
+ onCheckChange(e, item.category, [permission.value])
106
+ }}
107
+ >
108
+ {permission.label}
109
+ </Checkbox>
110
+ </Col>
111
+ ))}
112
+ </Row>
113
+ </Checkbox.Group>
114
+ ),
115
+ }))}
116
+ onChange={onCollapseChange}
117
+ />
118
+ )
119
+ }
120
+
121
+ export default PermissionCollapse
@@ -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