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.
- package/.eslintignore +2 -0
- package/.eslintrc.js +4 -0
- package/.turbo/turbo-build.log +20 -0
- package/CHANGELOG.md +10 -0
- package/dist/512_orange_nobackground-L6MFCL6M.png +0 -0
- package/dist/index.css +230 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +191 -0
- package/dist/index.esm.js +3152 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +63 -0
- package/postcss.config.js +9 -0
- package/src/assets/512_orange_nobackground.png +0 -0
- package/src/components/DynamicTags/index.tsx +160 -0
- package/src/components/FilterForm/index.tsx +62 -0
- package/src/components/FormModal/hooks.tsx +48 -0
- package/src/components/FormModal/index.tsx +138 -0
- package/src/components/Highlight/index.tsx +51 -0
- package/src/components/PermissionButton/index.tsx +36 -0
- package/src/components/QueryList/index.tsx +158 -0
- package/src/components/index.ts +27 -0
- package/src/constants/index.ts +3 -0
- package/src/features/permission/components/PermissionList.tsx +129 -0
- package/src/features/permission/hooks/index.ts +140 -0
- package/src/features/permission/index.ts +5 -0
- package/src/features/permission/types/index.ts +33 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/use-fetcher.tsx +102 -0
- package/src/hooks/use-permission.tsx +58 -0
- package/src/index.ts +7 -0
- package/src/layouts/Layout.tsx +81 -0
- package/src/layouts/NavBar.tsx +176 -0
- package/src/layouts/index.ts +6 -0
- package/src/pages/Login/default.tsx +864 -0
- package/src/pages/Login/index.tsx +111 -0
- package/src/pages/index.ts +4 -0
- package/src/pages/permission/RoleDetail.tsx +40 -0
- package/src/pages/permission/RoleList.tsx +226 -0
- package/src/pages/permission/UserList.tsx +248 -0
- package/src/pages/permission/index.tsx +31 -0
- package/src/shims.d.ts +20 -0
- package/src/stores/index.ts +3 -0
- package/src/stores/menu.ts +27 -0
- package/src/stores/queryTrigger.ts +27 -0
- package/src/stores/token.ts +25 -0
- package/src/styles/index.css +5 -0
- package/src/types/index.ts +27 -0
- package/tailwind.config.js +5 -0
- package/tsconfig.json +11 -0
- package/tsup.config.ts +26 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { useMenuStore } from '@/stores'
|
|
3
|
+
import Icon, * as Icons from '@ant-design/icons'
|
|
4
|
+
import { Menu } from 'antd'
|
|
5
|
+
import type {
|
|
6
|
+
ItemType,
|
|
7
|
+
MenuDividerType,
|
|
8
|
+
MenuItemGroupType,
|
|
9
|
+
MenuItemType,
|
|
10
|
+
SubMenuType,
|
|
11
|
+
} from 'antd/es/menu/hooks/useItems'
|
|
12
|
+
import type { FC, ForwardRefExoticComponent, ReactNode } from 'react'
|
|
13
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
14
|
+
import { Link, useLocation } from 'react-router-dom'
|
|
15
|
+
import type { Merge } from 'ts-essentials'
|
|
16
|
+
|
|
17
|
+
// 扩展 antd Menu 的类型,使其支持一些我们想要的自定义字段。
|
|
18
|
+
type MenuItemType2 = Merge<
|
|
19
|
+
Omit<MenuItemType, 'icon'>,
|
|
20
|
+
{
|
|
21
|
+
code /** 权限编号 **/?: string
|
|
22
|
+
route /** 前端路由地址 **/?: string
|
|
23
|
+
icon?: string
|
|
24
|
+
}
|
|
25
|
+
>
|
|
26
|
+
|
|
27
|
+
type SubMenuType2 = Merge<
|
|
28
|
+
Omit<SubMenuType, 'icon'>,
|
|
29
|
+
{
|
|
30
|
+
children?: ItemType2[]
|
|
31
|
+
icon?: string
|
|
32
|
+
}
|
|
33
|
+
>
|
|
34
|
+
|
|
35
|
+
type MenuItemGroupType2 = Merge<MenuItemGroupType, { children?: ItemType2[] }>
|
|
36
|
+
|
|
37
|
+
export type ItemType2 = MenuItemType2 | SubMenuType2 | MenuItemGroupType2 | MenuDividerType | null
|
|
38
|
+
|
|
39
|
+
const withLink = (label?: ReactNode, route?: string): ReactNode => {
|
|
40
|
+
if (!label) {
|
|
41
|
+
return <></>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (route) {
|
|
45
|
+
return <Link to={route}>{label}</Link>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return label
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 转换导航配置,主要做了以下几件事情
|
|
53
|
+
* 1. 用 Link 元素包装 route
|
|
54
|
+
* 2. 收集 code,用于权限判断
|
|
55
|
+
*/
|
|
56
|
+
function transformItems(items: ItemType2[]) {
|
|
57
|
+
const result: ItemType[] = []
|
|
58
|
+
|
|
59
|
+
for (let i = 0; i < items.length; i++) {
|
|
60
|
+
if (items[i] === null) {
|
|
61
|
+
result[i] = null
|
|
62
|
+
} else if ((items[i] as MenuDividerType).type === 'divider') {
|
|
63
|
+
result[i] = { ...items[i] } as MenuDividerType
|
|
64
|
+
} else {
|
|
65
|
+
// 引入 icon
|
|
66
|
+
const iconName = (items[i] as MenuItemType2 | SubMenuType2).icon
|
|
67
|
+
|
|
68
|
+
if ((items[i] as SubMenuType2 | MenuItemGroupType2).children) {
|
|
69
|
+
const { children, ...restProps } = items[i] as SubMenuType2 | MenuItemGroupType2
|
|
70
|
+
result[i] = {
|
|
71
|
+
...restProps,
|
|
72
|
+
children: transformItems(children ?? []),
|
|
73
|
+
icon: iconName ? <Icon component={(Icons as any)[iconName] as ForwardRefExoticComponent<any>} /> : null,
|
|
74
|
+
} as SubMenuType | MenuItemGroupType
|
|
75
|
+
} else {
|
|
76
|
+
const { route, label, code, ...restProps } = items[i] as MenuItemType2
|
|
77
|
+
// const isPass = code
|
|
78
|
+
// ? await httpClient.post<PermissionCheckResult>('/usystem/user/check', { permissions: [code] }).then(res => {
|
|
79
|
+
// if (res.has_all) {
|
|
80
|
+
// return true
|
|
81
|
+
// }
|
|
82
|
+
//
|
|
83
|
+
// return res[code] ?? false
|
|
84
|
+
// })
|
|
85
|
+
// : true
|
|
86
|
+
|
|
87
|
+
const isPass = true
|
|
88
|
+
|
|
89
|
+
result[i] = isPass
|
|
90
|
+
? ({
|
|
91
|
+
...restProps,
|
|
92
|
+
label: withLink(label, route),
|
|
93
|
+
icon: iconName ? <Icon component={(Icons as any)[iconName] as ForwardRefExoticComponent<any>} /> : null,
|
|
94
|
+
} as MenuItemType)
|
|
95
|
+
: null
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 拍平导航配置,并且注入 keypath 字段
|
|
104
|
+
function flatItems(
|
|
105
|
+
items: ItemType2[],
|
|
106
|
+
result: Merge<MenuItemType2, { keypath?: string[] }>[] = [],
|
|
107
|
+
keypath: string[] = [],
|
|
108
|
+
) {
|
|
109
|
+
for (const item of items) {
|
|
110
|
+
const children = (item as SubMenuType2 | MenuItemGroupType2)!.children as ItemType2[]
|
|
111
|
+
|
|
112
|
+
if (Array.isArray(children)) {
|
|
113
|
+
const _keys =
|
|
114
|
+
(item as MenuItemGroupType2)!.type !== 'group' && item!.key ? [...keypath, item!.key as string] : keypath
|
|
115
|
+
flatItems(children, result, _keys)
|
|
116
|
+
} else {
|
|
117
|
+
result.push(Object.assign(item as MenuItemType2, { keypath }))
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return result
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface NavBarProps {
|
|
125
|
+
items: ItemType2[]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const NavBar: FC<NavBarProps> = props => {
|
|
129
|
+
const { items } = props
|
|
130
|
+
const location = useLocation()
|
|
131
|
+
const flattenItems = useMemo(() => flatItems(items ?? []), [items])
|
|
132
|
+
const [internalItems, setInternalItems] = useState<ItemType<MenuItemType>[]>([])
|
|
133
|
+
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
setInternalItems(transformItems(items ?? []))
|
|
136
|
+
}, [items])
|
|
137
|
+
|
|
138
|
+
const openKeys = useMenuStore(state => state.openKeys)
|
|
139
|
+
const selectedKeys = useMenuStore(state => state.selectedKeys)
|
|
140
|
+
const setOpenKeys = useMenuStore(state => state.setOpenKeys)
|
|
141
|
+
const setSelectedKeys = useMenuStore(state => state.setSelectedKeys)
|
|
142
|
+
|
|
143
|
+
const onOpenChange = useCallback(
|
|
144
|
+
(keys: string[]) => {
|
|
145
|
+
const latestOpenKey = keys?.find(key => openKeys?.indexOf(key) === -1)
|
|
146
|
+
const match = flattenItems.find(item => latestOpenKey === item.key)
|
|
147
|
+
const _openKeys = (match?.keypath ?? [latestOpenKey]) as string[]
|
|
148
|
+
setOpenKeys(_openKeys)
|
|
149
|
+
},
|
|
150
|
+
[flattenItems, openKeys, setOpenKeys],
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
const match = flattenItems.find(item => location.pathname === item.route)
|
|
155
|
+
|
|
156
|
+
if (match) {
|
|
157
|
+
const key = match.key as string
|
|
158
|
+
const keypath = match.keypath as string[]
|
|
159
|
+
setSelectedKeys([key])
|
|
160
|
+
setOpenKeys(keypath)
|
|
161
|
+
}
|
|
162
|
+
}, [flattenItems, location, setOpenKeys, setSelectedKeys])
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<Menu
|
|
166
|
+
style={{ borderRight: 'none' }}
|
|
167
|
+
items={internalItems}
|
|
168
|
+
mode="inline"
|
|
169
|
+
openKeys={openKeys}
|
|
170
|
+
selectedKeys={selectedKeys}
|
|
171
|
+
onOpenChange={onOpenChange}
|
|
172
|
+
/>
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export default NavBar
|