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.
- package/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +6 -0
- package/README.md +41 -0
- package/dist/index.css +251 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +55 -54
- package/dist/index.esm.js +3574 -15
- package/dist/index.esm.js.map +1 -1
- package/package.json +2 -1
- package/src/components/GameSelect/index.tsx +82 -0
- package/src/components/Layout/index.tsx +90 -0
- package/src/{layouts/NavBar.tsx → components/NavMenu/index.tsx} +8 -17
- package/src/components/ReactToolkitsProvider/context.ts +76 -0
- package/src/components/ReactToolkitsProvider/index.tsx +23 -0
- package/src/components/UserWidget/index.tsx +46 -0
- package/src/components/index.ts +25 -1
- package/src/features/permission/components/PermissionCollapse/index.tsx +121 -0
- package/src/features/permission/components/PermissionList/index.tsx +17 -118
- package/src/features/permission/components/PermissionListV1/index.tsx +42 -0
- package/src/features/permission/components/PermissionListV2/index.tsx +146 -0
- package/src/features/permission/hooks/index.ts +24 -9
- package/src/features/permission/types/index.ts +8 -1
- package/src/hooks/use-http-client.tsx +9 -0
- package/src/hooks/use-permission.tsx +10 -2
- package/src/index.ts +0 -1
- package/src/pages/{NoMatch → base/NotFound}/index.tsx +4 -4
- package/src/pages/base/index.tsx +20 -0
- package/src/pages/index.ts +3 -4
- package/src/pages/permission/RoleList/index.tsx +63 -36
- package/src/pages/permission/index.tsx +26 -1
- package/src/stores/index.ts +0 -1
- package/src/stores/token.ts +15 -1
- package/tsup.config.ts +1 -1
- package/src/layouts/Layout.tsx +0 -103
- package/src/layouts/index.ts +0 -6
- package/src/stores/menu.ts +0 -27
- /package/src/pages/{Login → base/Login}/default.tsx +0 -0
- /package/src/pages/{Login → base/Login}/index.tsx +0 -0
|
@@ -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 {
|
|
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
|
|
19
|
-
const {
|
|
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
|
|
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:
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
86
|
+
return useFormModal<{
|
|
61
87
|
id: number
|
|
62
88
|
name: string
|
|
63
|
-
permissions:
|
|
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
|
-
|
|
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<
|
|
134
|
-
|
|
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,
|
|
@@ -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,
|
package/src/stores/index.ts
CHANGED
package/src/stores/token.ts
CHANGED
|
@@ -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: '' })
|
package/tsup.config.ts
CHANGED
package/src/layouts/Layout.tsx
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import logo from '@/assets/512_orange_nobackground.png'
|
|
2
|
-
import { Alert, Layout as AntdLayout, Spin, theme } from 'antd'
|
|
3
|
-
import type { FC, PropsWithChildren, ReactNode } from 'react'
|
|
4
|
-
import { Suspense } from 'react'
|
|
5
|
-
import { Link } from 'react-router-dom'
|
|
6
|
-
import type { ItemType2 } from './NavBar'
|
|
7
|
-
import NavBar from './NavBar'
|
|
8
|
-
import { usePermission } from '@/hooks'
|
|
9
|
-
|
|
10
|
-
const { Header, Sider, Content } = AntdLayout
|
|
11
|
-
const { ErrorBoundary } = Alert
|
|
12
|
-
|
|
13
|
-
export interface LayoutProps {
|
|
14
|
-
title?: ReactNode
|
|
15
|
-
items: ItemType2[]
|
|
16
|
-
header?: ReactNode
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const Layout: FC<PropsWithChildren<LayoutProps>> = props => {
|
|
20
|
-
const { title, items, header, children } = props
|
|
21
|
-
const {
|
|
22
|
-
token: { colorBgContainer, colorBorder },
|
|
23
|
-
} = theme.useToken()
|
|
24
|
-
|
|
25
|
-
// 为了验证 token 是否过期的请求,此处无其他用处。
|
|
26
|
-
usePermission('100001')
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<AntdLayout hasSider className="h-screen">
|
|
30
|
-
<ErrorBoundary>
|
|
31
|
-
<Suspense
|
|
32
|
-
fallback={
|
|
33
|
-
<Spin
|
|
34
|
-
style={{
|
|
35
|
-
display: 'flex',
|
|
36
|
-
justifyContent: 'center',
|
|
37
|
-
alignItems: 'center',
|
|
38
|
-
width: '100vw',
|
|
39
|
-
height: '100vh',
|
|
40
|
-
}}
|
|
41
|
-
/>
|
|
42
|
-
}
|
|
43
|
-
>
|
|
44
|
-
<Sider
|
|
45
|
-
width={256}
|
|
46
|
-
style={{
|
|
47
|
-
overflow: 'auto',
|
|
48
|
-
height: '100vh',
|
|
49
|
-
position: 'fixed',
|
|
50
|
-
left: 0,
|
|
51
|
-
top: 0,
|
|
52
|
-
bottom: 0,
|
|
53
|
-
borderRightWidth: 1,
|
|
54
|
-
borderRightStyle: 'solid',
|
|
55
|
-
borderRightColor: colorBorder,
|
|
56
|
-
}}
|
|
57
|
-
theme="light"
|
|
58
|
-
>
|
|
59
|
-
<div className="flex items-end px-6 py-4">
|
|
60
|
-
<img src={logo} alt="logo" className="w-8 h-8" />
|
|
61
|
-
<Link className="font-bold text-lg ml-2" to="/">
|
|
62
|
-
{title}
|
|
63
|
-
</Link>
|
|
64
|
-
</div>
|
|
65
|
-
|
|
66
|
-
<NavBar items={items} />
|
|
67
|
-
</Sider>
|
|
68
|
-
<AntdLayout className="ml-64">
|
|
69
|
-
<Header
|
|
70
|
-
style={{
|
|
71
|
-
padding: '0 24px',
|
|
72
|
-
background: colorBgContainer,
|
|
73
|
-
borderBottomWidth: 1,
|
|
74
|
-
borderBottomStyle: 'solid',
|
|
75
|
-
borderBottomColor: colorBorder,
|
|
76
|
-
}}
|
|
77
|
-
>
|
|
78
|
-
{header}
|
|
79
|
-
</Header>
|
|
80
|
-
<Content className="p-6 overflow-auto bg-gray-50">
|
|
81
|
-
<Suspense
|
|
82
|
-
fallback={
|
|
83
|
-
<Spin
|
|
84
|
-
style={{
|
|
85
|
-
display: 'flex',
|
|
86
|
-
justifyContent: 'center',
|
|
87
|
-
alignItems: 'center',
|
|
88
|
-
height: '50vh',
|
|
89
|
-
}}
|
|
90
|
-
/>
|
|
91
|
-
}
|
|
92
|
-
>
|
|
93
|
-
{children}
|
|
94
|
-
</Suspense>
|
|
95
|
-
</Content>
|
|
96
|
-
</AntdLayout>
|
|
97
|
-
</Suspense>
|
|
98
|
-
</ErrorBoundary>
|
|
99
|
-
</AntdLayout>
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export default Layout
|
package/src/layouts/index.ts
DELETED
package/src/stores/menu.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { create } from 'zustand'
|
|
2
|
-
import { persist } from 'zustand/middleware'
|
|
3
|
-
|
|
4
|
-
export interface MenuState {
|
|
5
|
-
openKeys: string[]
|
|
6
|
-
selectedKeys: string[]
|
|
7
|
-
setOpenKeys: (keys: string[]) => void
|
|
8
|
-
setSelectedKeys: (keys: string[]) => void
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const useMenuStore = create<MenuState>()(
|
|
12
|
-
persist(
|
|
13
|
-
set => ({
|
|
14
|
-
openKeys: [],
|
|
15
|
-
selectedKeys: [],
|
|
16
|
-
setOpenKeys: keys => set({ openKeys: keys }),
|
|
17
|
-
setSelectedKeys: keys => set({ selectedKeys: keys }),
|
|
18
|
-
}),
|
|
19
|
-
{
|
|
20
|
-
name: 'menu',
|
|
21
|
-
partialize: state => ({
|
|
22
|
-
openKeys: state.openKeys,
|
|
23
|
-
selectedKeys: state.selectedKeys,
|
|
24
|
-
}),
|
|
25
|
-
},
|
|
26
|
-
),
|
|
27
|
-
)
|
|
File without changes
|
|
File without changes
|