create-packer 1.28.0 → 1.29.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/package.json +1 -1
- package/template/web-app/react/package.json +3 -3
- package/template/web-app/react/shared/hooks/defineRouter/defineRouter.types.ts +3 -0
- package/template/web-app/react/shared/hooks/defineRouter/deineRouter.tsx +160 -160
- package/template/web-app/react-webpack/package.json +3 -3
- package/template/web-app/react-webpack/shared/hooks/defineRouter/defineRouter.types.ts +3 -0
- package/template/web-app/react-webpack/shared/hooks/defineRouter/deineRouter.tsx +160 -160
- package/template/web-extension/package.json +1 -1
- package/template/web-extension/shared/service/tools/base.ts +15 -5
- package/template/web-extension/shared/service/tools/createRequestActions.ts +4 -4
- package/template/web-extension/shared/service/tools/createService.ts +10 -5
- package/template/web-extension/shared/service/tools/hooks.ts +73 -67
- package/template/web-extension/shared/service/types.ts +0 -4
package/package.json
CHANGED
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
"commit": "git add . && npm run cz"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"1k-types": "1.
|
|
21
|
+
"1k-types": "1.2.0",
|
|
22
22
|
"axios": "1.3.6",
|
|
23
23
|
"define-zustand": "3.1.0",
|
|
24
24
|
"immer": "10.0.1",
|
|
25
25
|
"lodash-es": "4.17.21",
|
|
26
26
|
"qs": "6.11.2",
|
|
27
|
-
"react": "18.
|
|
28
|
-
"react-dom": "18.
|
|
27
|
+
"react": "18.3.1",
|
|
28
|
+
"react-dom": "18.3.1",
|
|
29
29
|
"react-router-dom": "6.14.0",
|
|
30
30
|
"react-use": "17.5.0",
|
|
31
31
|
"zustand": "4.4.1"
|
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
import { DependencyList, useEffect, useMemo } from 'react'
|
|
2
|
-
import { useMatches, useSearchParams, NavigateOptions, createBrowserRouter } from 'react-router-dom'
|
|
3
|
-
import { assign, isArray, reduce, get, map, split, omit, cloneDeep, last, forEach } from 'lodash-es'
|
|
4
|
-
import { stringify, parse } from 'qs'
|
|
5
|
-
import { defineStore } from 'define-zustand'
|
|
6
|
-
import { routeByIdType, routeType, editableRouteType } from './defineRouter.types'
|
|
7
|
-
|
|
8
|
-
export default function defineRouter(router: ReturnType<typeof createBrowserRouter>) {
|
|
9
|
-
const useRouter = defineStore({
|
|
10
|
-
state: () => ({
|
|
11
|
-
routes: cloneDeep(router.routes) as routeType[]
|
|
12
|
-
}),
|
|
13
|
-
getter: {
|
|
14
|
-
routesById: state => {
|
|
15
|
-
return (function flat(routes: routeType[], parentRoute?: routeByIdType) {
|
|
16
|
-
return reduce(
|
|
17
|
-
routes,
|
|
18
|
-
(result, { children, ...route }, i) => {
|
|
19
|
-
const $route: routeByIdType = {
|
|
20
|
-
...route,
|
|
21
|
-
pos: parentRoute?.pos ? `${parentRoute?.pos}-${i}` : `${i}`
|
|
22
|
-
}
|
|
23
|
-
if (parentRoute) {
|
|
24
|
-
$route.path = `${
|
|
25
|
-
parentRoute.path === '/' ? '' : parentRoute.path
|
|
26
|
-
}/${$route.path}`
|
|
27
|
-
}
|
|
28
|
-
result[$route.id] = $route
|
|
29
|
-
if (isArray(children)) {
|
|
30
|
-
assign(result, flat(children, $route))
|
|
31
|
-
}
|
|
32
|
-
return result
|
|
33
|
-
},
|
|
34
|
-
{} as Record<string, routeByIdType>
|
|
35
|
-
)
|
|
36
|
-
})(state.routes)
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
actions: (getState, action) => {
|
|
40
|
-
function posToLodashPath(pos: string) {
|
|
41
|
-
if (pos) {
|
|
42
|
-
return `[${split(pos, '-').join('].children[')}]`
|
|
43
|
-
}
|
|
44
|
-
return ''
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function updateRoute(id: routeType['id'], updator: (route: editableRouteType) => void) {
|
|
48
|
-
const { routesById, routes } = getState()
|
|
49
|
-
const newRoutes = cloneDeep(routes)
|
|
50
|
-
const path = posToLodashPath(routesById[id].pos)
|
|
51
|
-
const route: routeType = get(newRoutes, path)
|
|
52
|
-
const newRoute = cloneDeep(omit(route, ['element', 'errorElement', 'children']))
|
|
53
|
-
updator(newRoute)
|
|
54
|
-
assign(route, newRoute)
|
|
55
|
-
action.setState({ routes: newRoutes })
|
|
56
|
-
}
|
|
57
|
-
function getRoute(id: routeType['id'], path?: string | string[]) {
|
|
58
|
-
const { routesById } = getState()
|
|
59
|
-
const route = routesById[id]
|
|
60
|
-
if (path) {
|
|
61
|
-
return get(route, path)
|
|
62
|
-
}
|
|
63
|
-
return route
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function genRouteUrl(id: routeType['id'], query?: Record<string, any>) {
|
|
67
|
-
const path = getRoute(id, 'path')
|
|
68
|
-
if (path) {
|
|
69
|
-
const { origin } = window.location
|
|
70
|
-
let url = origin + router.basename + path
|
|
71
|
-
url += query ? `?${stringify(query)}` : ''
|
|
72
|
-
return url
|
|
73
|
-
}
|
|
74
|
-
return void 0
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function openRoute(id: routeType['id'], query?: Record<string, any>) {
|
|
78
|
-
const url = genRouteUrl(id, query)
|
|
79
|
-
if (url) {
|
|
80
|
-
window.open(url)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function reloadRoute(id: routeType['id'], query?: Record<string, any>) {
|
|
85
|
-
const url = genRouteUrl(id, query)
|
|
86
|
-
if (url) {
|
|
87
|
-
window.location.replace(url)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function navigate(
|
|
92
|
-
to: { id: routeType['id']; query?: Record<string, any> },
|
|
93
|
-
opts?: NavigateOptions
|
|
94
|
-
) {
|
|
95
|
-
return router.navigate(
|
|
96
|
-
{
|
|
97
|
-
pathname: getRoute(to.id, 'path'),
|
|
98
|
-
search: to.query ? stringify(to.query) : ''
|
|
99
|
-
},
|
|
100
|
-
opts
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
navigate,
|
|
106
|
-
getRoute,
|
|
107
|
-
genRouteUrl,
|
|
108
|
-
openRoute,
|
|
109
|
-
reloadRoute,
|
|
110
|
-
updateRoute
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
function useQuery<Q>() {
|
|
115
|
-
const [params] = useSearchParams()
|
|
116
|
-
const query = parse(params.toString())
|
|
117
|
-
|
|
118
|
-
return query as Q
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function useMatchRoutes(): routeType[] {
|
|
122
|
-
const matches = useMatches()
|
|
123
|
-
const getRoute = useRouter(state => state.getRoute)
|
|
124
|
-
return useMemo(() => map(matches, o => getRoute(o.id)), [matches])
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* @description 验证器返回false则无权限
|
|
129
|
-
*/
|
|
130
|
-
function useRoutePermission(
|
|
131
|
-
config: {
|
|
132
|
-
redirectRouteId: string
|
|
133
|
-
validator: (currentRoute?: routeType) => boolean
|
|
134
|
-
},
|
|
135
|
-
deps: DependencyList
|
|
136
|
-
) {
|
|
137
|
-
const matchRoutes = useMatchRoutes()
|
|
138
|
-
const currentRoute = last(matchRoutes)
|
|
139
|
-
const updateRoute = useRouter(state => state.updateRoute)
|
|
140
|
-
const navigate = useRouter(state => state.navigate)
|
|
141
|
-
const routesById = useRouter(state => state.routesById)
|
|
142
|
-
|
|
143
|
-
useEffect(() => {
|
|
144
|
-
forEach(routesById, route => {
|
|
145
|
-
updateRoute(route.id, route => {
|
|
146
|
-
route.unauthorized = !config.validator(route)
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
}, deps)
|
|
150
|
-
|
|
151
|
-
useEffect(() => {
|
|
152
|
-
const result = config.validator(currentRoute)
|
|
153
|
-
if (!result && config.redirectRouteId) {
|
|
154
|
-
navigate({ id: config.redirectRouteId }, { replace: true })
|
|
155
|
-
}
|
|
156
|
-
}, [currentRoute?.id, ...deps])
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return { useRouter, useQuery, useMatchRoutes, useRoutePermission }
|
|
160
|
-
}
|
|
1
|
+
import { DependencyList, useEffect, useMemo } from 'react'
|
|
2
|
+
import { useMatches, useSearchParams, NavigateOptions, createBrowserRouter } from 'react-router-dom'
|
|
3
|
+
import { assign, isArray, reduce, get, map, split, omit, cloneDeep, last, forEach } from 'lodash-es'
|
|
4
|
+
import { stringify, parse } from 'qs'
|
|
5
|
+
import { defineStore } from 'define-zustand'
|
|
6
|
+
import { routeByIdType, routeType, editableRouteType } from './defineRouter.types'
|
|
7
|
+
|
|
8
|
+
export default function defineRouter(router: ReturnType<typeof createBrowserRouter>) {
|
|
9
|
+
const useRouter = defineStore({
|
|
10
|
+
state: () => ({
|
|
11
|
+
routes: cloneDeep(router.routes) as routeType[]
|
|
12
|
+
}),
|
|
13
|
+
getter: {
|
|
14
|
+
routesById: state => {
|
|
15
|
+
return (function flat(routes: routeType[], parentRoute?: routeByIdType) {
|
|
16
|
+
return reduce(
|
|
17
|
+
routes,
|
|
18
|
+
(result, { children, ...route }, i) => {
|
|
19
|
+
const $route: routeByIdType = {
|
|
20
|
+
...route,
|
|
21
|
+
pos: parentRoute?.pos ? `${parentRoute?.pos}-${i}` : `${i}`
|
|
22
|
+
}
|
|
23
|
+
if (parentRoute) {
|
|
24
|
+
$route.path = `${
|
|
25
|
+
parentRoute.path === '/' ? '' : parentRoute.path
|
|
26
|
+
}/${$route.path}`
|
|
27
|
+
}
|
|
28
|
+
result[$route.id] = $route
|
|
29
|
+
if (isArray(children)) {
|
|
30
|
+
assign(result, flat(children, $route))
|
|
31
|
+
}
|
|
32
|
+
return result
|
|
33
|
+
},
|
|
34
|
+
{} as Record<string, routeByIdType>
|
|
35
|
+
)
|
|
36
|
+
})(state.routes)
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
actions: (getState, action) => {
|
|
40
|
+
function posToLodashPath(pos: string) {
|
|
41
|
+
if (pos) {
|
|
42
|
+
return `[${split(pos, '-').join('].children[')}]`
|
|
43
|
+
}
|
|
44
|
+
return ''
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function updateRoute(id: routeType['id'], updator: (route: editableRouteType) => void) {
|
|
48
|
+
const { routesById, routes } = getState()
|
|
49
|
+
const newRoutes = cloneDeep(routes)
|
|
50
|
+
const path = posToLodashPath(routesById[id].pos)
|
|
51
|
+
const route: routeType = get(newRoutes, path)
|
|
52
|
+
const newRoute = cloneDeep(omit(route, ['element', 'errorElement', 'children']))
|
|
53
|
+
updator(newRoute)
|
|
54
|
+
assign(route, newRoute)
|
|
55
|
+
action.setState({ routes: newRoutes })
|
|
56
|
+
}
|
|
57
|
+
function getRoute(id: routeType['id'], path?: string | string[]) {
|
|
58
|
+
const { routesById } = getState()
|
|
59
|
+
const route = routesById[id]
|
|
60
|
+
if (path) {
|
|
61
|
+
return get(route, path)
|
|
62
|
+
}
|
|
63
|
+
return route
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function genRouteUrl(id: routeType['id'], query?: Record<string, any>) {
|
|
67
|
+
const path = getRoute(id, 'path')
|
|
68
|
+
if (path) {
|
|
69
|
+
const { origin } = window.location
|
|
70
|
+
let url = origin + router.basename + path
|
|
71
|
+
url += query ? `?${stringify(query)}` : ''
|
|
72
|
+
return url
|
|
73
|
+
}
|
|
74
|
+
return void 0
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function openRoute(id: routeType['id'], query?: Record<string, any>) {
|
|
78
|
+
const url = genRouteUrl(id, query)
|
|
79
|
+
if (url) {
|
|
80
|
+
window.open(url)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function reloadRoute(id: routeType['id'], query?: Record<string, any>) {
|
|
85
|
+
const url = genRouteUrl(id, query)
|
|
86
|
+
if (url) {
|
|
87
|
+
window.location.replace(url)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function navigate(
|
|
92
|
+
to: { id: routeType['id']; query?: Record<string, any> },
|
|
93
|
+
opts?: NavigateOptions
|
|
94
|
+
) {
|
|
95
|
+
return router.navigate(
|
|
96
|
+
{
|
|
97
|
+
pathname: getRoute(to.id, 'path'),
|
|
98
|
+
search: to.query ? stringify(to.query) : ''
|
|
99
|
+
},
|
|
100
|
+
opts
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
navigate,
|
|
106
|
+
getRoute,
|
|
107
|
+
genRouteUrl,
|
|
108
|
+
openRoute,
|
|
109
|
+
reloadRoute,
|
|
110
|
+
updateRoute
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
function useQuery<Q>() {
|
|
115
|
+
const [params] = useSearchParams()
|
|
116
|
+
const query = parse(params.toString())
|
|
117
|
+
|
|
118
|
+
return query as Q
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function useMatchRoutes(): routeType[] {
|
|
122
|
+
const matches = useMatches()
|
|
123
|
+
const getRoute = useRouter(state => state.getRoute)
|
|
124
|
+
return useMemo(() => map(matches, o => getRoute(o.id)), [matches])
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @description 验证器返回false则无权限
|
|
129
|
+
*/
|
|
130
|
+
function useRoutePermission(
|
|
131
|
+
config: {
|
|
132
|
+
redirectRouteId: string
|
|
133
|
+
validator: (currentRoute?: routeType) => boolean
|
|
134
|
+
},
|
|
135
|
+
deps: DependencyList
|
|
136
|
+
) {
|
|
137
|
+
const matchRoutes = useMatchRoutes()
|
|
138
|
+
const currentRoute = last(matchRoutes)
|
|
139
|
+
const updateRoute = useRouter(state => state.updateRoute)
|
|
140
|
+
const navigate = useRouter(state => state.navigate)
|
|
141
|
+
const routesById = useRouter(state => state.routesById)
|
|
142
|
+
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
forEach(routesById, route => {
|
|
145
|
+
updateRoute(route.id, route => {
|
|
146
|
+
route.unauthorized = !config.validator(route)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
}, deps)
|
|
150
|
+
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
const result = config.validator(currentRoute)
|
|
153
|
+
if (!result && config.redirectRouteId) {
|
|
154
|
+
navigate({ id: config.redirectRouteId }, { replace: true })
|
|
155
|
+
}
|
|
156
|
+
}, [currentRoute?.id, ...deps])
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { useRouter, useQuery, useMatchRoutes, useRoutePermission }
|
|
160
|
+
}
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"commit": "git add . && npm run cz"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"1k-types": "1.
|
|
22
|
+
"1k-types": "1.2.0",
|
|
23
23
|
"axios": "1.6.7",
|
|
24
24
|
"define-zustand": "3.1.0",
|
|
25
25
|
"immer": "10.0.3",
|
|
26
26
|
"lodash-es": "4.17.21",
|
|
27
27
|
"qs": "6.11.2",
|
|
28
|
-
"react": "18.
|
|
29
|
-
"react-dom": "18.
|
|
28
|
+
"react": "18.3.1",
|
|
29
|
+
"react-dom": "18.3.1",
|
|
30
30
|
"react-router-dom": "6.14.0",
|
|
31
31
|
"react-use": "17.5.0",
|
|
32
32
|
"tailwindcss": "3.4.1",
|
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
import { DependencyList, useEffect, useMemo } from 'react'
|
|
2
|
-
import { useMatches, useSearchParams, NavigateOptions, createBrowserRouter } from 'react-router-dom'
|
|
3
|
-
import { assign, isArray, reduce, get, map, split, omit, cloneDeep, last, forEach } from 'lodash-es'
|
|
4
|
-
import { stringify, parse } from 'qs'
|
|
5
|
-
import { defineStore } from 'define-zustand'
|
|
6
|
-
import { routeByIdType, routeType, editableRouteType } from './defineRouter.types'
|
|
7
|
-
|
|
8
|
-
export default function defineRouter(router: ReturnType<typeof createBrowserRouter>) {
|
|
9
|
-
const useRouter = defineStore({
|
|
10
|
-
state: () => ({
|
|
11
|
-
routes: cloneDeep(router.routes) as routeType[]
|
|
12
|
-
}),
|
|
13
|
-
getter: {
|
|
14
|
-
routesById: state => {
|
|
15
|
-
return (function flat(routes: routeType[], parentRoute?: routeByIdType) {
|
|
16
|
-
return reduce(
|
|
17
|
-
routes,
|
|
18
|
-
(result, { children, ...route }, i) => {
|
|
19
|
-
const $route: routeByIdType = {
|
|
20
|
-
...route,
|
|
21
|
-
pos: parentRoute?.pos ? `${parentRoute?.pos}-${i}` : `${i}`
|
|
22
|
-
}
|
|
23
|
-
if (parentRoute) {
|
|
24
|
-
$route.path = `${
|
|
25
|
-
parentRoute.path === '/' ? '' : parentRoute.path
|
|
26
|
-
}/${$route.path}`
|
|
27
|
-
}
|
|
28
|
-
result[$route.id] = $route
|
|
29
|
-
if (isArray(children)) {
|
|
30
|
-
assign(result, flat(children, $route))
|
|
31
|
-
}
|
|
32
|
-
return result
|
|
33
|
-
},
|
|
34
|
-
{} as Record<string, routeByIdType>
|
|
35
|
-
)
|
|
36
|
-
})(state.routes)
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
actions: (getState, action) => {
|
|
40
|
-
function posToLodashPath(pos: string) {
|
|
41
|
-
if (pos) {
|
|
42
|
-
return `[${split(pos, '-').join('].children[')}]`
|
|
43
|
-
}
|
|
44
|
-
return ''
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function updateRoute(id: routeType['id'], updator: (route: editableRouteType) => void) {
|
|
48
|
-
const { routesById, routes } = getState()
|
|
49
|
-
const newRoutes = cloneDeep(routes)
|
|
50
|
-
const path = posToLodashPath(routesById[id].pos)
|
|
51
|
-
const route: routeType = get(newRoutes, path)
|
|
52
|
-
const newRoute = cloneDeep(omit(route, ['element', 'errorElement', 'children']))
|
|
53
|
-
updator(newRoute)
|
|
54
|
-
assign(route, newRoute)
|
|
55
|
-
action.setState({ routes: newRoutes })
|
|
56
|
-
}
|
|
57
|
-
function getRoute(id: routeType['id'], path?: string | string[]) {
|
|
58
|
-
const { routesById } = getState()
|
|
59
|
-
const route = routesById[id]
|
|
60
|
-
if (path) {
|
|
61
|
-
return get(route, path)
|
|
62
|
-
}
|
|
63
|
-
return route
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function genRouteUrl(id: routeType['id'], query?: Record<string, any>) {
|
|
67
|
-
const path = getRoute(id, 'path')
|
|
68
|
-
if (path) {
|
|
69
|
-
const { origin } = window.location
|
|
70
|
-
let url = origin + router.basename + path
|
|
71
|
-
url += query ? `?${stringify(query)}` : ''
|
|
72
|
-
return url
|
|
73
|
-
}
|
|
74
|
-
return void 0
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function openRoute(id: routeType['id'], query?: Record<string, any>) {
|
|
78
|
-
const url = genRouteUrl(id, query)
|
|
79
|
-
if (url) {
|
|
80
|
-
window.open(url)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function reloadRoute(id: routeType['id'], query?: Record<string, any>) {
|
|
85
|
-
const url = genRouteUrl(id, query)
|
|
86
|
-
if (url) {
|
|
87
|
-
window.location.replace(url)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function navigate(
|
|
92
|
-
to: { id: routeType['id']; query?: Record<string, any> },
|
|
93
|
-
opts?: NavigateOptions
|
|
94
|
-
) {
|
|
95
|
-
return router.navigate(
|
|
96
|
-
{
|
|
97
|
-
pathname: getRoute(to.id, 'path'),
|
|
98
|
-
search: to.query ? stringify(to.query) : ''
|
|
99
|
-
},
|
|
100
|
-
opts
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
navigate,
|
|
106
|
-
getRoute,
|
|
107
|
-
genRouteUrl,
|
|
108
|
-
openRoute,
|
|
109
|
-
reloadRoute,
|
|
110
|
-
updateRoute
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
function useQuery<Q>() {
|
|
115
|
-
const [params] = useSearchParams()
|
|
116
|
-
const query = parse(params.toString())
|
|
117
|
-
|
|
118
|
-
return query as Q
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function useMatchRoutes(): routeType[] {
|
|
122
|
-
const matches = useMatches()
|
|
123
|
-
const getRoute = useRouter(state => state.getRoute)
|
|
124
|
-
return useMemo(() => map(matches, o => getRoute(o.id)), [matches])
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* @description 验证器返回false则无权限
|
|
129
|
-
*/
|
|
130
|
-
function useRoutePermission(
|
|
131
|
-
config: {
|
|
132
|
-
redirectRouteId: string
|
|
133
|
-
validator: (currentRoute?: routeType) => boolean
|
|
134
|
-
},
|
|
135
|
-
deps: DependencyList
|
|
136
|
-
) {
|
|
137
|
-
const matchRoutes = useMatchRoutes()
|
|
138
|
-
const currentRoute = last(matchRoutes)
|
|
139
|
-
const updateRoute = useRouter(state => state.updateRoute)
|
|
140
|
-
const navigate = useRouter(state => state.navigate)
|
|
141
|
-
const routesById = useRouter(state => state.routesById)
|
|
142
|
-
|
|
143
|
-
useEffect(() => {
|
|
144
|
-
forEach(routesById, route => {
|
|
145
|
-
updateRoute(route.id, route => {
|
|
146
|
-
route.unauthorized = !config.validator(route)
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
}, deps)
|
|
150
|
-
|
|
151
|
-
useEffect(() => {
|
|
152
|
-
const result = config.validator(currentRoute)
|
|
153
|
-
if (!result && config.redirectRouteId) {
|
|
154
|
-
navigate({ id: config.redirectRouteId }, { replace: true })
|
|
155
|
-
}
|
|
156
|
-
}, [currentRoute?.id, ...deps])
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return { useRouter, useQuery, useMatchRoutes, useRoutePermission }
|
|
160
|
-
}
|
|
1
|
+
import { DependencyList, useEffect, useMemo } from 'react'
|
|
2
|
+
import { useMatches, useSearchParams, NavigateOptions, createBrowserRouter } from 'react-router-dom'
|
|
3
|
+
import { assign, isArray, reduce, get, map, split, omit, cloneDeep, last, forEach } from 'lodash-es'
|
|
4
|
+
import { stringify, parse } from 'qs'
|
|
5
|
+
import { defineStore } from 'define-zustand'
|
|
6
|
+
import { routeByIdType, routeType, editableRouteType } from './defineRouter.types'
|
|
7
|
+
|
|
8
|
+
export default function defineRouter(router: ReturnType<typeof createBrowserRouter>) {
|
|
9
|
+
const useRouter = defineStore({
|
|
10
|
+
state: () => ({
|
|
11
|
+
routes: cloneDeep(router.routes) as routeType[]
|
|
12
|
+
}),
|
|
13
|
+
getter: {
|
|
14
|
+
routesById: state => {
|
|
15
|
+
return (function flat(routes: routeType[], parentRoute?: routeByIdType) {
|
|
16
|
+
return reduce(
|
|
17
|
+
routes,
|
|
18
|
+
(result, { children, ...route }, i) => {
|
|
19
|
+
const $route: routeByIdType = {
|
|
20
|
+
...route,
|
|
21
|
+
pos: parentRoute?.pos ? `${parentRoute?.pos}-${i}` : `${i}`
|
|
22
|
+
}
|
|
23
|
+
if (parentRoute) {
|
|
24
|
+
$route.path = `${
|
|
25
|
+
parentRoute.path === '/' ? '' : parentRoute.path
|
|
26
|
+
}/${$route.path}`
|
|
27
|
+
}
|
|
28
|
+
result[$route.id] = $route
|
|
29
|
+
if (isArray(children)) {
|
|
30
|
+
assign(result, flat(children, $route))
|
|
31
|
+
}
|
|
32
|
+
return result
|
|
33
|
+
},
|
|
34
|
+
{} as Record<string, routeByIdType>
|
|
35
|
+
)
|
|
36
|
+
})(state.routes)
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
actions: (getState, action) => {
|
|
40
|
+
function posToLodashPath(pos: string) {
|
|
41
|
+
if (pos) {
|
|
42
|
+
return `[${split(pos, '-').join('].children[')}]`
|
|
43
|
+
}
|
|
44
|
+
return ''
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function updateRoute(id: routeType['id'], updator: (route: editableRouteType) => void) {
|
|
48
|
+
const { routesById, routes } = getState()
|
|
49
|
+
const newRoutes = cloneDeep(routes)
|
|
50
|
+
const path = posToLodashPath(routesById[id].pos)
|
|
51
|
+
const route: routeType = get(newRoutes, path)
|
|
52
|
+
const newRoute = cloneDeep(omit(route, ['element', 'errorElement', 'children']))
|
|
53
|
+
updator(newRoute)
|
|
54
|
+
assign(route, newRoute)
|
|
55
|
+
action.setState({ routes: newRoutes })
|
|
56
|
+
}
|
|
57
|
+
function getRoute(id: routeType['id'], path?: string | string[]) {
|
|
58
|
+
const { routesById } = getState()
|
|
59
|
+
const route = routesById[id]
|
|
60
|
+
if (path) {
|
|
61
|
+
return get(route, path)
|
|
62
|
+
}
|
|
63
|
+
return route
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function genRouteUrl(id: routeType['id'], query?: Record<string, any>) {
|
|
67
|
+
const path = getRoute(id, 'path')
|
|
68
|
+
if (path) {
|
|
69
|
+
const { origin } = window.location
|
|
70
|
+
let url = origin + router.basename + path
|
|
71
|
+
url += query ? `?${stringify(query)}` : ''
|
|
72
|
+
return url
|
|
73
|
+
}
|
|
74
|
+
return void 0
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function openRoute(id: routeType['id'], query?: Record<string, any>) {
|
|
78
|
+
const url = genRouteUrl(id, query)
|
|
79
|
+
if (url) {
|
|
80
|
+
window.open(url)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function reloadRoute(id: routeType['id'], query?: Record<string, any>) {
|
|
85
|
+
const url = genRouteUrl(id, query)
|
|
86
|
+
if (url) {
|
|
87
|
+
window.location.replace(url)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function navigate(
|
|
92
|
+
to: { id: routeType['id']; query?: Record<string, any> },
|
|
93
|
+
opts?: NavigateOptions
|
|
94
|
+
) {
|
|
95
|
+
return router.navigate(
|
|
96
|
+
{
|
|
97
|
+
pathname: getRoute(to.id, 'path'),
|
|
98
|
+
search: to.query ? stringify(to.query) : ''
|
|
99
|
+
},
|
|
100
|
+
opts
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
navigate,
|
|
106
|
+
getRoute,
|
|
107
|
+
genRouteUrl,
|
|
108
|
+
openRoute,
|
|
109
|
+
reloadRoute,
|
|
110
|
+
updateRoute
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
function useQuery<Q>() {
|
|
115
|
+
const [params] = useSearchParams()
|
|
116
|
+
const query = parse(params.toString())
|
|
117
|
+
|
|
118
|
+
return query as Q
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function useMatchRoutes(): routeType[] {
|
|
122
|
+
const matches = useMatches()
|
|
123
|
+
const getRoute = useRouter(state => state.getRoute)
|
|
124
|
+
return useMemo(() => map(matches, o => getRoute(o.id)), [matches])
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @description 验证器返回false则无权限
|
|
129
|
+
*/
|
|
130
|
+
function useRoutePermission(
|
|
131
|
+
config: {
|
|
132
|
+
redirectRouteId: string
|
|
133
|
+
validator: (currentRoute?: routeType) => boolean
|
|
134
|
+
},
|
|
135
|
+
deps: DependencyList
|
|
136
|
+
) {
|
|
137
|
+
const matchRoutes = useMatchRoutes()
|
|
138
|
+
const currentRoute = last(matchRoutes)
|
|
139
|
+
const updateRoute = useRouter(state => state.updateRoute)
|
|
140
|
+
const navigate = useRouter(state => state.navigate)
|
|
141
|
+
const routesById = useRouter(state => state.routesById)
|
|
142
|
+
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
forEach(routesById, route => {
|
|
145
|
+
updateRoute(route.id, route => {
|
|
146
|
+
route.unauthorized = !config.validator(route)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
}, deps)
|
|
150
|
+
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
const result = config.validator(currentRoute)
|
|
153
|
+
if (!result && config.redirectRouteId) {
|
|
154
|
+
navigate({ id: config.redirectRouteId }, { replace: true })
|
|
155
|
+
}
|
|
156
|
+
}, [currentRoute?.id, ...deps])
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { useRouter, useQuery, useMatchRoutes, useRoutePermission }
|
|
160
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { includes } from 'lodash-es'
|
|
1
2
|
import { httpBodyType } from '../types'
|
|
2
3
|
|
|
3
4
|
export class FetchError extends Error {
|
|
@@ -14,15 +15,24 @@ export async function pickRequestBody<Body = httpBodyType>(
|
|
|
14
15
|
req: Request
|
|
15
16
|
): Promise<Body | undefined> {
|
|
16
17
|
let reqBody = void 0
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (!includes(['GET', 'HEAD'], req.method)) {
|
|
19
|
+
const dataTypes = ['json', 'blob', 'text', 'formData'] as const
|
|
20
|
+
for (let i = 0; i < dataTypes.length; i++) {
|
|
21
|
+
try {
|
|
22
|
+
const dataType = dataTypes[i]
|
|
23
|
+
reqBody = await req.clone()[dataType]()
|
|
24
|
+
} catch {
|
|
25
|
+
/* empty */
|
|
26
|
+
}
|
|
27
|
+
if (reqBody) {
|
|
28
|
+
break
|
|
29
|
+
}
|
|
30
|
+
}
|
|
21
31
|
}
|
|
22
32
|
return reqBody
|
|
23
33
|
}
|
|
24
34
|
|
|
25
|
-
export async function
|
|
35
|
+
export async function pickResponseBody<Body = any>(res: Response): Promise<Body | undefined> {
|
|
26
36
|
let body = void 0
|
|
27
37
|
try {
|
|
28
38
|
body = await res.clone().json()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { KyInstance
|
|
2
|
-
import { Input } from 'ky/distribution/types/options'
|
|
1
|
+
import { KyInstance } from 'ky'
|
|
2
|
+
import { Input, HttpMethod } from 'ky/distribution/types/options'
|
|
3
3
|
import { includes, isFunction, isString, omit } from 'lodash-es'
|
|
4
4
|
import { stringify } from 'qs'
|
|
5
5
|
import { Nullable } from '1k-types'
|
|
@@ -12,7 +12,7 @@ export function createRequestActions(
|
|
|
12
12
|
request: KyInstance,
|
|
13
13
|
hooks: serviceHooksType
|
|
14
14
|
) {
|
|
15
|
-
function creator(method:
|
|
15
|
+
function creator(method: HttpMethod) {
|
|
16
16
|
return async function <DATA = any>(url: Input, option?: requestOptionsType): Promise<DATA> {
|
|
17
17
|
const globalPrefix = isFunction(prefixUrl) ? await prefixUrl() : prefixUrl
|
|
18
18
|
const $prefix = isFunction(option?.prefixUrl)
|
|
@@ -29,7 +29,7 @@ export function createRequestActions(
|
|
|
29
29
|
hooks: createKyRequestHooks(newHooks)
|
|
30
30
|
}
|
|
31
31
|
try {
|
|
32
|
-
const res = request[method](url, newOption)
|
|
32
|
+
const res = request[method](url, newOption) as any
|
|
33
33
|
if (option?.responseType === 'response') {
|
|
34
34
|
return await res
|
|
35
35
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ky from 'ky'
|
|
2
|
-
import { assign,
|
|
2
|
+
import { assign, isPlainObject } from 'lodash-es'
|
|
3
|
+
import { AnyObject } from '1k-types'
|
|
3
4
|
import { configType } from '../types'
|
|
4
5
|
import { createRequestActions } from './createRequestActions'
|
|
5
6
|
import { createServiceHooks } from './hooks'
|
|
@@ -27,11 +28,15 @@ export function createService(config: configType) {
|
|
|
27
28
|
// init
|
|
28
29
|
// ------------------------------------------------------------------------
|
|
29
30
|
addHooks('beforeRequest', async req => {
|
|
30
|
-
const
|
|
31
|
-
req.searchParams = {
|
|
31
|
+
const body = isPlainObject(req.body) ? (req.body as AnyObject) : void 0
|
|
32
|
+
req.searchParams = {
|
|
33
|
+
...globalSearchParams
|
|
34
|
+
}
|
|
32
35
|
|
|
33
|
-
if (
|
|
34
|
-
req.body = {
|
|
36
|
+
if (isPlainObject(body)) {
|
|
37
|
+
req.body = {
|
|
38
|
+
...globalParams
|
|
39
|
+
}
|
|
35
40
|
}
|
|
36
41
|
})
|
|
37
42
|
|
|
@@ -1,67 +1,73 @@
|
|
|
1
|
-
import { Hooks } from 'ky'
|
|
2
|
-
import { omit } from 'lodash-es'
|
|
3
|
-
import { stringify, parse } from 'qs'
|
|
4
|
-
import { ArrayValues } from '1k-types'
|
|
5
|
-
import { serviceHooksType } from '../types'
|
|
6
|
-
import { pickRequestBody } from './base'
|
|
7
|
-
|
|
8
|
-
export function createServiceHooks(hooks?: Partial<serviceHooksType>) {
|
|
9
|
-
const serviceHooks: serviceHooksType = {
|
|
10
|
-
httpError: [],
|
|
11
|
-
beforeRequest: [],
|
|
12
|
-
afterResponse: [],
|
|
13
|
-
...(hooks || {})
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function addHooks<K extends keyof serviceHooksType>(
|
|
17
|
-
key: K,
|
|
18
|
-
callback: ArrayValues<serviceHooksType[K]>
|
|
19
|
-
) {
|
|
20
|
-
serviceHooks[key].push(callback as any)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
serviceHooks,
|
|
25
|
-
addHooks
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function createKyRequestHooks(serviceHooks: serviceHooksType): Hooks {
|
|
30
|
-
async function forEachHooks<K extends keyof serviceHooksType>(
|
|
31
|
-
name: K,
|
|
32
|
-
arg: Parameters<ArrayValues<serviceHooksType[K]>>[0]
|
|
33
|
-
) {
|
|
34
|
-
for (let i = 0; i < serviceHooks[name].length; i++) {
|
|
35
|
-
const fn = serviceHooks[name][i]
|
|
36
|
-
await fn(arg as never)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
beforeRequest: [
|
|
41
|
-
async req => {
|
|
42
|
-
// eslint-disable-next-line prefer-const
|
|
43
|
-
let [url, searchParams] = req.url.split('?')
|
|
44
|
-
const reqBody = await pickRequestBody(req)
|
|
45
|
-
const parsedSearchParams = parse(searchParams)
|
|
46
|
-
const reqConfig = {
|
|
47
|
-
searchParams: parsedSearchParams,
|
|
48
|
-
body: reqBody,
|
|
49
|
-
headers: req.headers,
|
|
50
|
-
method: req.method
|
|
51
|
-
}
|
|
52
|
-
await forEachHooks('beforeRequest', reqConfig)
|
|
53
|
-
url = url + stringify(reqConfig.searchParams, { addQueryPrefix: true })
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
1
|
+
import { Hooks } from 'ky'
|
|
2
|
+
import { isNumber, isString, omit, some, isPlainObject, isArray } from 'lodash-es'
|
|
3
|
+
import { stringify, parse } from 'qs'
|
|
4
|
+
import { ArrayValues } from '1k-types'
|
|
5
|
+
import { serviceHooksType } from '../types'
|
|
6
|
+
import { pickRequestBody } from './base'
|
|
7
|
+
|
|
8
|
+
export function createServiceHooks(hooks?: Partial<serviceHooksType>) {
|
|
9
|
+
const serviceHooks: serviceHooksType = {
|
|
10
|
+
httpError: [],
|
|
11
|
+
beforeRequest: [],
|
|
12
|
+
afterResponse: [],
|
|
13
|
+
...(hooks || {})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function addHooks<K extends keyof serviceHooksType>(
|
|
17
|
+
key: K,
|
|
18
|
+
callback: ArrayValues<serviceHooksType[K]>
|
|
19
|
+
) {
|
|
20
|
+
serviceHooks[key].push(callback as any)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
serviceHooks,
|
|
25
|
+
addHooks
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createKyRequestHooks(serviceHooks: serviceHooksType): Hooks {
|
|
30
|
+
async function forEachHooks<K extends keyof serviceHooksType>(
|
|
31
|
+
name: K,
|
|
32
|
+
arg: Parameters<ArrayValues<serviceHooksType[K]>>[0]
|
|
33
|
+
) {
|
|
34
|
+
for (let i = 0; i < serviceHooks[name].length; i++) {
|
|
35
|
+
const fn = serviceHooks[name][i]
|
|
36
|
+
await fn(arg as never)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
beforeRequest: [
|
|
41
|
+
async req => {
|
|
42
|
+
// eslint-disable-next-line prefer-const
|
|
43
|
+
let [url, searchParams] = req.url.split('?')
|
|
44
|
+
const reqBody = await pickRequestBody(req)
|
|
45
|
+
const parsedSearchParams = parse(searchParams)
|
|
46
|
+
const reqConfig = {
|
|
47
|
+
searchParams: parsedSearchParams,
|
|
48
|
+
body: reqBody,
|
|
49
|
+
headers: req.headers,
|
|
50
|
+
method: req.method
|
|
51
|
+
}
|
|
52
|
+
await forEachHooks('beforeRequest', reqConfig)
|
|
53
|
+
url = url + stringify(reqConfig.searchParams, { addQueryPrefix: true })
|
|
54
|
+
const isStringify = some([isNumber, isString, isPlainObject, isArray], fn => {
|
|
55
|
+
return fn(reqConfig.body)
|
|
56
|
+
})
|
|
57
|
+
if (isStringify) {
|
|
58
|
+
reqConfig.body = JSON.stringify(reqConfig.body)
|
|
59
|
+
}
|
|
60
|
+
return new Request(url, {
|
|
61
|
+
...omit(req, 'url'),
|
|
62
|
+
headers: reqConfig.headers,
|
|
63
|
+
body: (reqConfig.body ? reqConfig.body : void 0) as BodyInit
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
afterResponse: [
|
|
68
|
+
async (request, options, response) => {
|
|
69
|
+
await forEachHooks('afterResponse', { request, options, response })
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -39,8 +39,4 @@ export interface configType extends Pick<requestOptionsType, 'prefixUrl' | 'head
|
|
|
39
39
|
globalParams?: Record<string, any>
|
|
40
40
|
globalSearchParams?: Record<string, any>
|
|
41
41
|
hooks?: Partial<serviceHooksType>
|
|
42
|
-
/**
|
|
43
|
-
* @default true
|
|
44
|
-
* */
|
|
45
|
-
isClearEmptyData?: boolean
|
|
46
42
|
}
|