react-toolkits 0.0.1

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 (50) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.js +4 -0
  3. package/.turbo/turbo-build.log +20 -0
  4. package/CHANGELOG.md +10 -0
  5. package/dist/512_orange_nobackground-L6MFCL6M.png +0 -0
  6. package/dist/index.css +230 -0
  7. package/dist/index.css.map +1 -0
  8. package/dist/index.d.mts +191 -0
  9. package/dist/index.esm.js +3152 -0
  10. package/dist/index.esm.js.map +1 -0
  11. package/package.json +63 -0
  12. package/postcss.config.js +9 -0
  13. package/src/assets/512_orange_nobackground.png +0 -0
  14. package/src/components/DynamicTags/index.tsx +160 -0
  15. package/src/components/FilterForm/index.tsx +62 -0
  16. package/src/components/FormModal/hooks.tsx +48 -0
  17. package/src/components/FormModal/index.tsx +138 -0
  18. package/src/components/Highlight/index.tsx +51 -0
  19. package/src/components/PermissionButton/index.tsx +36 -0
  20. package/src/components/QueryList/index.tsx +158 -0
  21. package/src/components/index.ts +27 -0
  22. package/src/constants/index.ts +3 -0
  23. package/src/features/permission/components/PermissionList.tsx +129 -0
  24. package/src/features/permission/hooks/index.ts +140 -0
  25. package/src/features/permission/index.ts +5 -0
  26. package/src/features/permission/types/index.ts +33 -0
  27. package/src/hooks/index.ts +2 -0
  28. package/src/hooks/use-fetcher.tsx +102 -0
  29. package/src/hooks/use-permission.tsx +58 -0
  30. package/src/index.ts +7 -0
  31. package/src/layouts/Layout.tsx +81 -0
  32. package/src/layouts/NavBar.tsx +176 -0
  33. package/src/layouts/index.ts +6 -0
  34. package/src/pages/Login/default.tsx +864 -0
  35. package/src/pages/Login/index.tsx +111 -0
  36. package/src/pages/index.ts +4 -0
  37. package/src/pages/permission/RoleDetail.tsx +40 -0
  38. package/src/pages/permission/RoleList.tsx +226 -0
  39. package/src/pages/permission/UserList.tsx +248 -0
  40. package/src/pages/permission/index.tsx +31 -0
  41. package/src/shims.d.ts +20 -0
  42. package/src/stores/index.ts +3 -0
  43. package/src/stores/menu.ts +27 -0
  44. package/src/stores/queryTrigger.ts +27 -0
  45. package/src/stores/token.ts +25 -0
  46. package/src/styles/index.css +5 -0
  47. package/src/types/index.ts +27 -0
  48. package/tailwind.config.js +5 -0
  49. package/tsconfig.json +11 -0
  50. package/tsup.config.ts +26 -0
@@ -0,0 +1,111 @@
1
+ import { SSO_URL } from '@/constants'
2
+ import { useTokenStore } from '@/stores'
3
+ import { AliyunOutlined } from '@ant-design/icons'
4
+ import { Alert, Button, Card, Col, Divider, Row, Space, Spin, Typography } from 'antd'
5
+ import type { FC, PropsWithChildren } from 'react'
6
+ import { useEffect, useState } from 'react'
7
+ import { Navigate, useSearchParams } from 'react-router-dom'
8
+ import useSWRImmutable from 'swr/immutable'
9
+ import Default from './default'
10
+
11
+ const { Title } = Typography
12
+
13
+ const Login: FC<PropsWithChildren> = props => {
14
+ const { children } = props
15
+ const [searchParams, setSearchParams] = useSearchParams()
16
+ const token = useTokenStore(state => state.token)
17
+ const setToken = useTokenStore(state => state.setToken)
18
+ const [showAlert, setShowAlert] = useState(false)
19
+
20
+ const { isLoading } = useSWRImmutable<{ token: string }>(
21
+ searchParams.has('ticket')
22
+ ? {
23
+ url: '/api/usystem/user/login',
24
+ params: {
25
+ ticket: searchParams.get('ticket'),
26
+ },
27
+ }
28
+ : null,
29
+ {
30
+ onSuccess: data => {
31
+ setToken(data.token)
32
+ },
33
+ },
34
+ )
35
+
36
+ useEffect(() => {
37
+ if (searchParams.has('not_registered')) {
38
+ setShowAlert(true)
39
+ searchParams.delete('not_registered')
40
+ setSearchParams(searchParams)
41
+ }
42
+ }, [searchParams, setSearchParams])
43
+
44
+ if (isLoading) {
45
+ return (
46
+ <Spin
47
+ style={{
48
+ display: 'flex',
49
+ justifyContent: 'center',
50
+ alignItems: 'center',
51
+ width: '100vw',
52
+ height: '100vh',
53
+ }}
54
+ />
55
+ )
56
+ }
57
+
58
+ if (token) {
59
+ return <Navigate replace to="/" />
60
+ }
61
+
62
+ return (
63
+ <Row>
64
+ <Col span={10} offset={3}>
65
+ <div className="h-screen flex justify-end items-center">
66
+ <Default />
67
+ </div>
68
+ </Col>
69
+ <Col span={5} offset={3}>
70
+ <div className="h-screen relative">
71
+ <Card hoverable className="absolute left-0 right-0 top-1/2 -translate-y-1/2">
72
+ {showAlert && (
73
+ <div className="absolute -top-12 left-0 right-0">
74
+ <Alert
75
+ banner
76
+ closable
77
+ message="您还未在平台注册,请联系管理员"
78
+ type="error"
79
+ onClose={() => {
80
+ setShowAlert(false)
81
+ }}
82
+ />
83
+ </div>
84
+ )}
85
+ <div className="text-center mb-6">
86
+ <Title level={5}>登录方式</Title>
87
+ <div className="min-h-10">{children}</div>
88
+ </div>
89
+ <Divider plain>第三方登录方式</Divider>
90
+ <div className="w-full flex justify-center">
91
+ <Space size="small">
92
+ <Button
93
+ type="link"
94
+ size="small"
95
+ shape="round"
96
+ icon={<AliyunOutlined />}
97
+ href={`${SSO_URL}/login?service=${encodeURIComponent(window.location.origin)}/login`}
98
+ target="_self"
99
+ >
100
+ IDass 登录
101
+ </Button>
102
+ </Space>
103
+ </div>
104
+ </Card>
105
+ </div>
106
+ </Col>
107
+ </Row>
108
+ )
109
+ }
110
+
111
+ export default Login
@@ -0,0 +1,4 @@
1
+ import Login from './Login'
2
+ import permission from './permission'
3
+
4
+ export { Login, permission }
@@ -0,0 +1,40 @@
1
+ import { PermissionList, useRole } from '@/features/permission'
2
+ import { Breadcrumb, Card, Descriptions, Skeleton } from 'antd'
3
+ import { Link, useParams } from 'react-router-dom'
4
+
5
+ const RoleDetail = () => {
6
+ const params = useParams()
7
+ const { data, isLoading } = useRole(params.name as string)
8
+
9
+ return (
10
+ <>
11
+ <Breadcrumb
12
+ style={{ marginBottom: 24 }}
13
+ items={[
14
+ {
15
+ key: '1',
16
+ title: <Link to="/permission/role">角色</Link>,
17
+ },
18
+ {
19
+ key: '2',
20
+ title: params.name,
21
+ },
22
+ ]}
23
+ />
24
+ <Card title="权限详情">
25
+ <Skeleton loading={isLoading}>
26
+ <Descriptions column={3} layout="vertical">
27
+ <Descriptions.Item label="名称">{data?.name}</Descriptions.Item>
28
+ <Descriptions.Item label="ID">{data?.id}</Descriptions.Item>
29
+ <Descriptions.Item label="创建时间">{data?.ctime}</Descriptions.Item>
30
+ <Descriptions.Item label="权限" span={3}>
31
+ <PermissionList readonly value={data?.permissions} />
32
+ </Descriptions.Item>
33
+ </Descriptions>
34
+ </Skeleton>
35
+ </Card>
36
+ </>
37
+ )
38
+ }
39
+
40
+ export default RoleDetail
@@ -0,0 +1,226 @@
1
+ import { Highlight, PermissionButton, QueryList } from '@/components'
2
+ import { useFormModal } from '@/components/FormModal/hooks'
3
+ import type { Role, RoleListItem } from '@/features/permission'
4
+ import { PermissionList, useCreateRole, useRemoveRole, useUpdateRole } from '@/features/permission'
5
+ import { useFetcher, usePermission } from '@/hooks'
6
+ import { useQueryTriggerStore } from '@/stores'
7
+ import { UsergroupAddOutlined } from '@ant-design/icons'
8
+ import type { TableColumnsType } from 'antd'
9
+ import { App, Card, Form, Input, Space } from 'antd'
10
+ import { useMemo } from 'react'
11
+ import { Link } from 'react-router-dom'
12
+
13
+ export const swrKey = {
14
+ url: '/api/usystem/role/list',
15
+ }
16
+
17
+ const RoleList = () => {
18
+ const { accessible: viewable } = usePermission('200005')
19
+ const { modal, message } = App.useApp()
20
+ const fetcher = useFetcher()
21
+ const create = useCreateRole()
22
+ const remove = useRemoveRole()
23
+ const update = useUpdateRole()
24
+ const trigger = useQueryTriggerStore(state => state.trigger)
25
+
26
+ const { showModal: showCreateModal, Modal: CreateModal } = useFormModal<{
27
+ name: string
28
+ permissions: string[]
29
+ }>({
30
+ title: '创建角色',
31
+ width: '50vw',
32
+ layout: 'vertical',
33
+ content: (
34
+ <>
35
+ <Form.Item label="名称" name="name" rules={[{ required: true }]}>
36
+ <Input addonBefore="role_" />
37
+ </Form.Item>
38
+ <Form.Item label="权限" name="permissions">
39
+ <PermissionList />
40
+ </Form.Item>
41
+ </>
42
+ ),
43
+ async onConfirm(values) {
44
+ await create.trigger(
45
+ {
46
+ name: `role_${values.name}`,
47
+ permissions: values.permissions,
48
+ },
49
+ {
50
+ async onSuccess() {
51
+ await message.success('角色创建成功')
52
+ trigger(swrKey)
53
+ },
54
+ },
55
+ )
56
+ },
57
+ })
58
+
59
+ const { showModal: showUpdateModal, Modal: UpdateModal } = useFormModal<{
60
+ id: number
61
+ name: string
62
+ permissions: string[]
63
+ }>({
64
+ title: '更新角色',
65
+ width: '50vw',
66
+ layout: 'vertical',
67
+ content: (
68
+ <>
69
+ <Form.Item hidden label="ID" name="id">
70
+ <Input />
71
+ </Form.Item>
72
+ <Form.Item label="名称" name="name" rules={[{ required: true }]}>
73
+ <Input disabled addonBefore="role_" />
74
+ </Form.Item>
75
+ <Form.Item label="权限" name="permissions">
76
+ <PermissionList />
77
+ </Form.Item>
78
+ </>
79
+ ),
80
+ async onConfirm(values) {
81
+ await update.trigger(
82
+ {
83
+ id: values.id,
84
+ name: `role_${values.name}`,
85
+ permissions: values.permissions,
86
+ },
87
+ {
88
+ async onSuccess() {
89
+ await message.success('角色更新成功')
90
+ trigger(swrKey)
91
+ },
92
+ },
93
+ )
94
+ },
95
+ })
96
+
97
+ const columns = useMemo<TableColumnsType<RoleListItem>>(
98
+ () => [
99
+ {
100
+ title: '名称',
101
+ key: 'name',
102
+ render(value: RoleListItem) {
103
+ if (viewable) {
104
+ return <Link to={`${value.name}`}>{value.name}</Link>
105
+ } else {
106
+ return <>{value.name}</>
107
+ }
108
+ },
109
+ },
110
+ {
111
+ title: 'ID',
112
+ dataIndex: 'id',
113
+ key: 'id',
114
+ },
115
+ {
116
+ title: '创建时间',
117
+ dataIndex: 'ctime',
118
+ key: 'ctime',
119
+ },
120
+ {
121
+ title: '操作',
122
+ width: 150,
123
+ align: 'center',
124
+ render: (value: RoleListItem) => {
125
+ return (
126
+ <Space size="small">
127
+ <PermissionButton
128
+ code="200003"
129
+ size="small"
130
+ type="link"
131
+ onClick={async () => {
132
+ const role = await fetcher<Role>({
133
+ method: 'GET',
134
+ url: '/api/usystem/role/info',
135
+ params: { name: value.name },
136
+ })
137
+ showUpdateModal({
138
+ initialValues: {
139
+ id: role?.id,
140
+ permissions: role?.permissions,
141
+ name: role?.name.replace(/^role_/, ''),
142
+ },
143
+ })
144
+ }}
145
+ >
146
+ 更新
147
+ </PermissionButton>
148
+ <PermissionButton
149
+ danger
150
+ code="200004"
151
+ size="small"
152
+ type="link"
153
+ onClick={() => {
154
+ modal.confirm({
155
+ title: '删除角色',
156
+ content: <Highlight texts={[value.name]}>
157
+ 确定要删除角色&nbsp;
158
+ {value.name}
159
+ &nbsp;吗?
160
+ </Highlight>,
161
+ async onOk() {
162
+ await remove.trigger(
163
+ {
164
+ id: value.id,
165
+ name: value.name,
166
+ },
167
+ {
168
+ async onSuccess() {
169
+ await message.success('角色删除成功')
170
+ trigger(swrKey)
171
+ },
172
+ },
173
+ )
174
+ },
175
+ })
176
+ }}
177
+ >
178
+ 删除
179
+ </PermissionButton>
180
+ </Space>
181
+ )
182
+ },
183
+ },
184
+ ],
185
+ [trigger, viewable, fetcher, modal, message, remove, showUpdateModal],
186
+ )
187
+
188
+ return (
189
+ <>
190
+ <Card
191
+ title="角色"
192
+ extra={
193
+ <PermissionButton
194
+ type="primary"
195
+ code="200002"
196
+ icon={<UsergroupAddOutlined />}
197
+ onClick={() => {
198
+ showCreateModal()
199
+ }}
200
+ >
201
+ 创建角色
202
+ </PermissionButton>
203
+ }
204
+ >
205
+ <QueryList
206
+ rowKey="name"
207
+ columns={columns}
208
+ code="200001"
209
+ swrKey={swrKey}
210
+ transformArg={arg => {
211
+ const { page, perPage, ...restValues } = arg
212
+ return {
213
+ ...restValues,
214
+ page,
215
+ size: perPage,
216
+ }
217
+ }}
218
+ />
219
+ </Card>
220
+ {CreateModal}
221
+ {UpdateModal}
222
+ </>
223
+ )
224
+ }
225
+
226
+ export default RoleList
@@ -0,0 +1,248 @@
1
+ import { Highlight, PermissionButton, QueryList } from '@/components'
2
+ import { useFormModal } from '@/components/FormModal/hooks'
3
+ import type { UserListItem } from '@/features/permission'
4
+ import { useAllRoles, useCreateUser, useRemoveUser, useUpdateUser } from '@/features/permission'
5
+ import { useQueryTriggerStore } from '@/stores'
6
+ import { UserAddOutlined } from '@ant-design/icons'
7
+ import type { TableColumnsType } from 'antd'
8
+ import { App, Card, Col, Form, Input, Row, Select, Space, Tag } from 'antd'
9
+ import type { FC } from 'react'
10
+ import { useMemo } from 'react'
11
+ import { Link } from 'react-router-dom'
12
+
13
+ const { Option } = Select
14
+
15
+ export const swrKey = {
16
+ url: '/api/usystem/user/list',
17
+ }
18
+
19
+ function useCreatingUserModal() {
20
+ const { message } = App.useApp()
21
+ const create = useCreateUser()
22
+ const { data: roles, isLoading } = useAllRoles()
23
+ const trigger = useQueryTriggerStore(state => state.trigger)
24
+
25
+ return useFormModal<{ id: string; name: string; roles: string[] }>({
26
+ title: '创建角色',
27
+ labelCol: { flex: '80px' },
28
+ content: (
29
+ <>
30
+ <Form.Item noStyle shouldUpdate={(prevValues, currentValue) => prevValues.type !== currentValue.type}>
31
+ {({ getFieldValue }) => (
32
+ <Form.Item label="名称" name="name" rules={[{ required: true }]}>
33
+ <Input disabled={getFieldValue('id')} />
34
+ </Form.Item>
35
+ )}
36
+ </Form.Item>
37
+ <Form.Item label="角色" name="roles">
38
+ <Select allowClear mode="multiple" loading={isLoading}>
39
+ {(roles ?? []).map(role => (
40
+ <Option value={role.name} key={role.id}>
41
+ {role.name}
42
+ </Option>
43
+ ))}
44
+ </Select>
45
+ </Form.Item>
46
+ </>
47
+ ),
48
+ async onConfirm(values) {
49
+ await create.trigger(values, {
50
+ onSuccess() {
51
+ message.success('用户创建成功')
52
+ trigger(swrKey)
53
+ },
54
+ })
55
+ },
56
+ })
57
+ }
58
+
59
+ function useUpdatingUserModal() {
60
+ const { message } = App.useApp()
61
+ const update = useUpdateUser()
62
+ const { data: roles, isLoading } = useAllRoles()
63
+ const trigger = useQueryTriggerStore(state => state.trigger)
64
+
65
+ return useFormModal<{ id: string; name: string; roles: string[] }>({
66
+ title: '更新角色',
67
+ labelCol: { flex: '80px' },
68
+ content: (
69
+ <>
70
+ <Form.Item hidden name="id">
71
+ <Input />
72
+ </Form.Item>
73
+ <Form.Item noStyle shouldUpdate={(prevValues, currentValue) => prevValues.type !== currentValue.type}>
74
+ {({ getFieldValue }) => (
75
+ <Form.Item label="名称" name="name" rules={[{ required: true }]}>
76
+ <Input disabled={getFieldValue('id')} />
77
+ </Form.Item>
78
+ )}
79
+ </Form.Item>
80
+ <Form.Item label="角色" name="roles">
81
+ <Select allowClear mode="multiple" loading={isLoading}>
82
+ {(roles ?? []).map(role => (
83
+ <Option value={role.name} key={role.id}>
84
+ {role.name}
85
+ </Option>
86
+ ))}
87
+ </Select>
88
+ </Form.Item>
89
+ </>
90
+ ),
91
+ async onConfirm(values) {
92
+ await update.trigger(values, {
93
+ onSuccess() {
94
+ message.success('用户更新成功')
95
+ trigger(swrKey)
96
+ },
97
+ })
98
+ },
99
+ })
100
+ }
101
+
102
+ const UserList: FC = () => {
103
+ const { modal, message } = App.useApp()
104
+ const remove = useRemoveUser()
105
+ const trigger = useQueryTriggerStore(state => state.trigger)
106
+ const { showModal: showCreatingModal, Modal: CreatingModal } = useCreatingUserModal()
107
+ const { showModal: showUpdatingModal, Modal: UpdatingModal } = useUpdatingUserModal()
108
+
109
+ const columns = useMemo<TableColumnsType<UserListItem>>(() => {
110
+ return [
111
+ {
112
+ title: '名称',
113
+ dataIndex: 'name',
114
+ key: 'name',
115
+ },
116
+ {
117
+ title: 'ID',
118
+ dataIndex: 'id',
119
+ key: 'id',
120
+ },
121
+ {
122
+ title: '角色',
123
+ dataIndex: 'roles',
124
+ key: 'roles',
125
+ width: '40%',
126
+ render(value: string[]) {
127
+ return (
128
+ <Row gutter={[4, 4]}>
129
+ {(value || []).map((item: string) => (
130
+ <Col key={item}>
131
+ {item === 'root' ? (
132
+ <Tag color="#f50">{item}</Tag>
133
+ ) : (
134
+ <Tag color="#ff5a00">
135
+ <Link to={`/permission/role/${item}`}>{item}</Link>
136
+ </Tag>
137
+ )}
138
+ </Col>
139
+ ))}
140
+ </Row>
141
+ )
142
+ },
143
+ },
144
+ {
145
+ title: '创建时间',
146
+ dataIndex: 'Ctime',
147
+ key: 'ctime',
148
+ },
149
+ {
150
+ title: '操作',
151
+ width: 150,
152
+ align: 'center',
153
+ render: (value: UserListItem) => (
154
+ <Space>
155
+ <PermissionButton
156
+ size="small"
157
+ type="link"
158
+ code="100003"
159
+ onClick={() => {
160
+ showUpdatingModal({
161
+ initialValues: {
162
+ id: value.id,
163
+ name: value.name,
164
+ roles: value.roles,
165
+ },
166
+ })
167
+ }}
168
+ >
169
+ 更新
170
+ </PermissionButton>
171
+ <PermissionButton
172
+ danger
173
+ size="small"
174
+ code="100004"
175
+ type="link"
176
+ onClick={() => {
177
+ modal.confirm({
178
+ title: '删除用户',
179
+ content: <Highlight texts={[value.name]}>
180
+ 确定要删除用户&nbsp;
181
+ {value.name}
182
+ &nbsp;吗?
183
+ </Highlight>,
184
+ async onOk() {
185
+ await remove.trigger(
186
+ {
187
+ id: value.id,
188
+ name: value.name,
189
+ },
190
+ {
191
+ async onSuccess() {
192
+ await message.success('用户删除成功')
193
+ trigger(swrKey)
194
+ },
195
+ },
196
+ )
197
+ },
198
+ })
199
+ }}
200
+ >
201
+ 删除
202
+ </PermissionButton>
203
+ </Space>
204
+ ),
205
+ },
206
+ ]
207
+ }, [trigger, remove, message, modal, showUpdatingModal])
208
+
209
+ return (
210
+ <>
211
+ <Card
212
+ title="用户"
213
+ extra={
214
+ <PermissionButton
215
+ type="primary"
216
+ icon={<UserAddOutlined />}
217
+ code="100002"
218
+ onClick={() => {
219
+ showCreatingModal()
220
+ }}
221
+ >
222
+ 创建用户
223
+ </PermissionButton>
224
+ }
225
+ >
226
+ <QueryList
227
+ code="100001"
228
+ swrKey={swrKey}
229
+ rowKey="id"
230
+ columns={columns}
231
+ transformArg={arg => {
232
+ const { page, perPage, ...restValues } = arg
233
+
234
+ return {
235
+ ...(restValues ?? {}),
236
+ page,
237
+ size: perPage,
238
+ }
239
+ }}
240
+ />
241
+ </Card>
242
+ {CreatingModal}
243
+ {UpdatingModal}
244
+ </>
245
+ )
246
+ }
247
+
248
+ export default UserList
@@ -0,0 +1,31 @@
1
+ import { lazy } from 'react'
2
+ import type { RouteObject } from 'react-router-dom'
3
+ import { Navigate } from 'react-router-dom'
4
+
5
+ const UserList = lazy(() => import('./UserList'))
6
+ const RoleList = lazy(() => import('./RoleList'))
7
+ const RoleDetail = lazy(() => import('./RoleDetail'))
8
+
9
+ const routes: RouteObject = {
10
+ path: 'permission',
11
+ children: [
12
+ {
13
+ index: true,
14
+ element: <Navigate relative="path" to="user" />,
15
+ },
16
+ {
17
+ path: 'user',
18
+ element: <UserList />,
19
+ },
20
+ {
21
+ path: 'role',
22
+ element: <RoleList />,
23
+ },
24
+ {
25
+ path: 'role/:name',
26
+ element: <RoleDetail />,
27
+ },
28
+ ],
29
+ }
30
+
31
+ export default routes
package/src/shims.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ declare module '*.jpg' {
3
+ const content: any
4
+ export default content
5
+ }
6
+
7
+ declare module '*.png' {
8
+ const content: any
9
+ export default content
10
+ }
11
+
12
+ declare module '*.svg' {
13
+ const content: any
14
+ export default content
15
+ }
16
+
17
+ declare module '*.yaml' {
18
+ const content: any
19
+ export default content
20
+ }
@@ -0,0 +1,3 @@
1
+ export * from './menu'
2
+ export * from './token'
3
+ export * from './queryTrigger'