@pubinfo/core 2.0.0-rc.3 → 2.0.0-rc.4
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/dist/{AppSetting-3wJKvibc.js → AppSetting-BI-oNc4e.js} +15 -15
- package/dist/{HCheckList.vue_vue_type_script_setup_true_lang-17EywJvs.js → HCheckList.vue_vue_type_script_setup_true_lang-BdLpkcoh.js} +1 -1
- package/dist/{HToggle-B-ZjSh6S.js → HToggle-DxdWLgp-.js} +1 -1
- package/dist/{PreferencesContent-xT4paU7N.js → PreferencesContent-CCYkZeCT.js} +4 -4
- package/dist/{SettingBreadcrumb-CYnO51Ek.js → SettingBreadcrumb-BTyfiy4k.js} +3 -3
- package/dist/{SettingCopyright-FOW5ObHK.js → SettingCopyright-g6UHi8pZ.js} +2 -2
- package/dist/{SettingEnableTransition-Q5cvubmF.js → SettingEnableTransition-Ci-5bhbR.js} +2 -2
- package/dist/{SettingHome-Df7-AlWB.js → SettingHome-K4Iel0Hr.js} +2 -2
- package/dist/{SettingMenu-BNAJ3el9.js → SettingMenu-BYLWzA5i.js} +3 -3
- package/dist/{SettingMode-LzlRsBL9.js → SettingMode-tRisyKtg.js} +1 -1
- package/dist/{SettingNavSearch-BA08vYuw.js → SettingNavSearch-CSM6mPf8.js} +2 -2
- package/dist/{SettingOther-BE8dDCYD.js → SettingOther-Bj5KF_vC.js} +2 -2
- package/dist/{SettingPage-D061yXCv.js → SettingPage-CFjmrVI7.js} +2 -2
- package/dist/{SettingTabbar-COwdPPKy.js → SettingTabbar-uFYiaZhK.js} +3 -3
- package/dist/{SettingThemes-BHaYERNp.js → SettingThemes-C-tMq9o5.js} +1 -1
- package/dist/{SettingToolbar-fSuzu6ND.js → SettingToolbar-BfDzijNU.js} +2 -2
- package/dist/{SettingTopbar-D7GgP0KB.js → SettingTopbar-DTDv4NXD.js} +3 -3
- package/dist/{SettingWidthMode-CNjzChe1.js → SettingWidthMode-PkiwrHe3.js} +1 -1
- package/dist/{TopThinMode-B-28sBJD.js → TopThinMode-BrvA8pV0.js} +1 -1
- package/dist/built-in/authentication/alova/helper.d.ts +34 -0
- package/dist/built-in/authentication/alova/token.d.ts +16 -0
- package/dist/built-in/authentication/helper.d.ts +10 -1
- package/dist/built-in/index.d.ts +0 -1
- package/dist/built-in/layout-component/components/ui/HSlideover.vue.d.ts +1 -1
- package/dist/built-in/layout-component/composables/useContext.d.ts +1 -1
- package/dist/built-in/layout-component/composables/useTitle.d.ts +1 -1
- package/dist/{colors-BIQSd520.js → colors-VoaDbOhe.js} +1 -1
- package/dist/core/interface.d.ts +14 -5
- package/dist/core/request.d.ts +2 -8
- package/dist/features/api/modules/auth/renzhengfuwu.d.ts +8 -8
- package/dist/features/api/modules/configData/heibaimingdanfuwu.d.ts +5 -5
- package/dist/features/api/modules/configData/xitongpeizhifuwu.d.ts +14 -14
- package/dist/features/api/modules/configData/zidifuwu.d.ts +10 -10
- package/dist/features/api/modules/rbac/gangweijiekou.d.ts +6 -6
- package/dist/features/api/modules/rbac/jiaosejiekou.d.ts +7 -7
- package/dist/features/api/modules/rbac/pubJiaosezukongzhiqi.d.ts +7 -7
- package/dist/features/api/modules/rbac/shujuquanxianzhubiaokongzhiqi.d.ts +9 -9
- package/dist/features/api/modules/rbac/yonghujiekou.d.ts +15 -15
- package/dist/features/api/modules/rbac/yonghushoucangbiaojiekou.d.ts +5 -5
- package/dist/features/api/modules/rbac/yonghuzuijinchangyongbiaojiekou.d.ts +4 -4
- package/dist/features/api/modules/rbac/ziyuanjiekou.d.ts +13 -13
- package/dist/features/api/modules/rbac/zuhuguanlijiekou.d.ts +5 -5
- package/dist/features/api/modules/rbac/zuzhijiaosebiaokongzhiqi.d.ts +4 -4
- package/dist/features/api/modules/rbac/zuzhijiekou.d.ts +9 -9
- package/dist/features/api/system/user.d.ts +4 -4
- package/dist/{index-YSjb6X1D.js → index-BSevJVD5.js} +4 -7
- package/dist/{index-Dlf6GQBd.js → index-BfGqLWFB.js} +4 -4
- package/dist/{index-DYMBkmAp.js → index-CYoFRwvw.js} +2 -2
- package/dist/{index-DNdH93AP.js → index-ConeY38N.js} +2 -2
- package/dist/{index-CPRiufg0.js → index-DV3hkzKA.js} +1 -1
- package/dist/{index-wxEEuQXu.js → index-Ddw98rJ5.js} +9 -9
- package/dist/{index-IscoZG-Y.js → index-DrC787X_.js} +2 -2
- package/dist/{index-WubcSL0v.js → index-Dv9ndBoi.js} +1 -1
- package/dist/{index-Beb7_0-E.js → index-IAYhIBQH.js} +16305 -16239
- package/dist/index.js +1 -1
- package/dist/{pick-D_XPbQHB.js → pick-vpv9EEvu.js} +1 -1
- package/dist/style.css +1 -1
- package/package.json +7 -7
- package/src/built-in/authentication/alova/helper.ts +158 -0
- package/src/built-in/authentication/alova/token.ts +122 -0
- package/src/built-in/authentication/helper.ts +7 -3
- package/src/built-in/authentication/index.ts +6 -20
- package/src/built-in/index.ts +0 -1
- package/src/built-in/layout-component/components/Header/TopMode/index.vue +3 -3
- package/src/built-in/layout-component/components/Menu/item.vue +3 -3
- package/src/built-in/layout-component/components/Sidebar/MainSidebar.vue +3 -3
- package/src/built-in/layout-component/components/Tools/SearchBar.vue +1 -3
- package/src/built-in/layout-component/components/Tools/SearchPanel.vue +12 -20
- package/src/built-in/layout-component/components/Tools/index.vue +8 -11
- package/src/built-in/layout-component/components/Topbar/Tabbar/MoreAction.vue +7 -10
- package/src/built-in/layout-component/components/Topbar/Tabbar/index.vue +10 -13
- package/src/built-in/layout-component/components/Topbar/Toolbar/Favorites.vue +4 -7
- package/src/built-in/layout-component/components/Topbar/Toolbar/index.vue +6 -6
- package/src/built-in/layout-component/composables/useContext.ts +1 -1
- package/src/built-in/layout-component/composables/useTitle.ts +8 -14
- package/src/built-in/layout-component/provider.ts +2 -2
- package/src/core/interface.ts +18 -5
- package/src/core/request.ts +35 -15
- package/src/features/router/systemRoutes.ts +0 -1
- package/src/features/stores/modules/favorites.ts +0 -1
- package/src/features/stores/modules/route.ts +2 -9
- package/src/features/stores/modules/tabbar.ts +0 -3
- package/src/features/stores/utils/routerHelper.ts +35 -4
- package/src/index.ts +0 -2
- package/types/vue-router.d.ts +0 -3
- package/dist/built-in/locales/helpler.d.ts +0 -594
- package/dist/built-in/locales/index.d.ts +0 -5
- package/dist/built-in/locales/lang/en.json.d.ts +0 -99
- package/dist/built-in/locales/lang/zh-cn.json.d.ts +0 -100
- package/dist/built-in/locales/lang/zh-tw.json.d.ts +0 -100
- package/dist/built-in/locales/ui.d.ts +0 -3
- package/src/built-in/locales/helpler.ts +0 -76
- package/src/built-in/locales/index.ts +0 -20
- package/src/built-in/locales/lang/en.json +0 -96
- package/src/built-in/locales/lang/zh-cn.json +0 -97
- package/src/built-in/locales/lang/zh-tw.json +0 -97
- package/src/built-in/locales/ui.ts +0 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pubinfo/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0.0-rc.
|
|
4
|
+
"version": "2.0.0-rc.4",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"alova": "^3.3.4",
|
|
23
23
|
"pinia": "^3.0.3",
|
|
24
24
|
"vue": "^3.5.17",
|
|
25
|
-
"vue-i18n": "^10.0.7",
|
|
26
25
|
"vue-router": "^4.5.1",
|
|
27
|
-
"@pubinfo/config": "2.0.0-rc.
|
|
28
|
-
"@pubinfo/vite": "2.0.0-rc.
|
|
26
|
+
"@pubinfo/config": "2.0.0-rc.4",
|
|
27
|
+
"@pubinfo/vite": "2.0.0-rc.4"
|
|
29
28
|
},
|
|
30
29
|
"dependencies": {
|
|
31
30
|
"@alova/adapter-axios": "^2.0.16",
|
|
31
|
+
"@alova/shared": "^1.3.1",
|
|
32
32
|
"@ant-design/icons-vue": "^7.0.1",
|
|
33
33
|
"@headlessui/vue": "^1.7.23",
|
|
34
34
|
"@imengyu/vue3-context-menu": "^1.5.1",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"zxcvbn": "^4.4.2"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
+
"@alova/mock": "^2.0.17",
|
|
61
62
|
"@iconify/json": "^2.2.354",
|
|
62
63
|
"@iconify/vue": "^5.0.0",
|
|
63
64
|
"@pubinfo/openapi": "^0.9.0",
|
|
@@ -80,10 +81,9 @@
|
|
|
80
81
|
"vite": "^7.1.2",
|
|
81
82
|
"vite-plugin-dts": "^4.5.4",
|
|
82
83
|
"vue": "^3.5.17",
|
|
83
|
-
"vue-i18n": "^10.0.7",
|
|
84
84
|
"vue-router": "^4.5.1",
|
|
85
|
-
"@pubinfo/config": "2.0.0-rc.
|
|
86
|
-
"@pubinfo/vite": "2.0.0-rc.
|
|
85
|
+
"@pubinfo/config": "2.0.0-rc.4",
|
|
86
|
+
"@pubinfo/vite": "2.0.0-rc.4"
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
89
89
|
"dev": "vite build -w -m watch",
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AlovaRequestAdapter,
|
|
3
|
+
Method,
|
|
4
|
+
RespondedHandler,
|
|
5
|
+
ResponseCompleteHandler,
|
|
6
|
+
ResponseErrorHandler,
|
|
7
|
+
StatesHook,
|
|
8
|
+
} from 'alova';
|
|
9
|
+
import type {
|
|
10
|
+
AlovaResponded,
|
|
11
|
+
MetaMatches,
|
|
12
|
+
ResponseAuthorizationInterceptor,
|
|
13
|
+
StateHookAdapter2AG,
|
|
14
|
+
} from 'alova/client';
|
|
15
|
+
import {
|
|
16
|
+
falseValue,
|
|
17
|
+
forEach,
|
|
18
|
+
instanceOf,
|
|
19
|
+
isFn,
|
|
20
|
+
isPlainObject,
|
|
21
|
+
len,
|
|
22
|
+
newInstance,
|
|
23
|
+
noop,
|
|
24
|
+
PromiseCls,
|
|
25
|
+
pushItem,
|
|
26
|
+
splice,
|
|
27
|
+
trueValue,
|
|
28
|
+
undefinedValue,
|
|
29
|
+
} from '@alova/shared';
|
|
30
|
+
|
|
31
|
+
export type PosibbleAuthMap
|
|
32
|
+
= | {
|
|
33
|
+
metaMatches?: MetaMatches
|
|
34
|
+
handler: any
|
|
35
|
+
}
|
|
36
|
+
| undefined;
|
|
37
|
+
export type WaitingRequestList = {
|
|
38
|
+
method: Method
|
|
39
|
+
resolve: () => void
|
|
40
|
+
}[];
|
|
41
|
+
|
|
42
|
+
export const defaultVisitorMeta = {
|
|
43
|
+
authRole: null,
|
|
44
|
+
};
|
|
45
|
+
export const defaultLoginMeta = {
|
|
46
|
+
authRole: 'login',
|
|
47
|
+
};
|
|
48
|
+
export const defaultLogoutMeta = {
|
|
49
|
+
authRole: 'logout',
|
|
50
|
+
};
|
|
51
|
+
export const defaultRefreshTokenMeta = {
|
|
52
|
+
authRole: 'refreshToken',
|
|
53
|
+
};
|
|
54
|
+
export function checkMethodRole({ meta }: Method, metaMatches: MetaMatches) {
|
|
55
|
+
if (isPlainObject(meta)) {
|
|
56
|
+
for (const key in meta) {
|
|
57
|
+
if (Object.prototype.hasOwnProperty.call(meta, key)) {
|
|
58
|
+
const matchedMetaItem = metaMatches[key];
|
|
59
|
+
if (instanceOf(matchedMetaItem, RegExp) ? matchedMetaItem.test(meta[key]) : meta[key] === matchedMetaItem) {
|
|
60
|
+
return trueValue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return falseValue;
|
|
66
|
+
}
|
|
67
|
+
export function waitForTokenRefreshed(method: Method, waitingList: WaitingRequestList) {
|
|
68
|
+
return newInstance(PromiseCls, (resolve) => {
|
|
69
|
+
pushItem(waitingList, {
|
|
70
|
+
method,
|
|
71
|
+
resolve,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
export function callHandlerIfMatchesMeta(method: Method,
|
|
76
|
+
authorizationInterceptor: ResponseAuthorizationInterceptor<AlovaRequestAdapter<any, any, any>> | undefined,
|
|
77
|
+
defaultMeta: MetaMatches,
|
|
78
|
+
response: any) {
|
|
79
|
+
if (checkMethodRole(method, (authorizationInterceptor as PosibbleAuthMap)?.metaMatches || defaultMeta)) {
|
|
80
|
+
const handler = isFn(authorizationInterceptor)
|
|
81
|
+
? authorizationInterceptor
|
|
82
|
+
: isPlainObject(authorizationInterceptor) && isFn(authorizationInterceptor.handler)
|
|
83
|
+
? authorizationInterceptor.handler
|
|
84
|
+
: noop;
|
|
85
|
+
return handler(response, method);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function refreshTokenIfExpired(method: Method,
|
|
89
|
+
waitingList: WaitingRequestList,
|
|
90
|
+
updateRefreshStatus: (status: boolean) => void,
|
|
91
|
+
handlerParams: any[],
|
|
92
|
+
refreshToken?: {
|
|
93
|
+
isExpired: (...args: any[]) => boolean | Promise<boolean>
|
|
94
|
+
handler: (...args: any[]) => Promise<void>
|
|
95
|
+
},
|
|
96
|
+
tokenRefreshing?: boolean) {
|
|
97
|
+
// When the number of handle params is greater than 2, it means that this function is called from the response, and the original interface needs to be requested again.
|
|
98
|
+
const fromResponse = len(handlerParams) >= 2;
|
|
99
|
+
let isExpired = refreshToken?.isExpired(...handlerParams);
|
|
100
|
+
// Compatible with synchronous and asynchronous functions
|
|
101
|
+
if (instanceOf(isExpired, PromiseCls)) {
|
|
102
|
+
isExpired = await isExpired;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isExpired) {
|
|
106
|
+
try {
|
|
107
|
+
// Make another judgment in the response to prevent multiple requests to refresh the token, intercept and wait for the token sent before the token refresh is completed.
|
|
108
|
+
let intentToRefreshToken = trueValue;
|
|
109
|
+
if (fromResponse && tokenRefreshing) {
|
|
110
|
+
intentToRefreshToken = falseValue; // The requests waiting here indicate that the token is being refreshed. When they pass, there is no need to refresh the token again.
|
|
111
|
+
await waitForTokenRefreshed(method, waitingList);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (intentToRefreshToken) {
|
|
115
|
+
updateRefreshStatus(trueValue);
|
|
116
|
+
// Call refresh token
|
|
117
|
+
await refreshToken?.handler(...handlerParams);
|
|
118
|
+
updateRefreshStatus(falseValue);
|
|
119
|
+
// After the token refresh is completed, the requests in the waiting list are notified.
|
|
120
|
+
forEach(waitingList, ({ resolve }) => resolve());
|
|
121
|
+
}
|
|
122
|
+
if (fromResponse) {
|
|
123
|
+
// Because the original interface is being requested again, superposition with the previous request will result in repeated calls to transform, so it is necessary to leave transform empty to remove one call.
|
|
124
|
+
const { config } = method;
|
|
125
|
+
const methodTransformData = config.transform;
|
|
126
|
+
config.transform = undefinedValue;
|
|
127
|
+
const resentData = await method;
|
|
128
|
+
config.transform = methodTransformData;
|
|
129
|
+
return resentData;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
updateRefreshStatus(falseValue);
|
|
134
|
+
splice(waitingList, 0, len(waitingList)); // Clear waiting list
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function onResponded2Record<SH extends StatesHook<any>, RA extends AlovaRequestAdapter<any, any, any>>(onRespondedHandlers?: AlovaResponded<SH, RA>) {
|
|
140
|
+
type AG = StateHookAdapter2AG<SH, RA>;
|
|
141
|
+
let successHandler: RespondedHandler<AG> | undefined = undefinedValue;
|
|
142
|
+
let errorHandler: ResponseErrorHandler<AG> | undefined = undefinedValue;
|
|
143
|
+
let onCompleteHandler: ResponseCompleteHandler<AG> | undefined = undefinedValue;
|
|
144
|
+
if (isFn(onRespondedHandlers)) {
|
|
145
|
+
successHandler = onRespondedHandlers;
|
|
146
|
+
}
|
|
147
|
+
else if (isPlainObject(onRespondedHandlers)) {
|
|
148
|
+
const { onSuccess, onError, onComplete } = onRespondedHandlers;
|
|
149
|
+
successHandler = isFn(onSuccess) ? onSuccess : successHandler;
|
|
150
|
+
errorHandler = isFn(onError) ? onError : errorHandler;
|
|
151
|
+
onCompleteHandler = isFn(onComplete) ? onComplete : onCompleteHandler;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
onSuccess: successHandler,
|
|
155
|
+
onError: errorHandler,
|
|
156
|
+
onComplete: onCompleteHandler,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { AlovaRequestAdapter, Method, StatesHook } from 'alova';
|
|
2
|
+
import type {
|
|
3
|
+
AlovaRequestAdapterUnified,
|
|
4
|
+
BeforeRequestType,
|
|
5
|
+
ServerTokenAuthenticationOptions,
|
|
6
|
+
} from 'alova/client';
|
|
7
|
+
import type { FetchRequestAdapter } from 'alova/fetch';
|
|
8
|
+
import type {
|
|
9
|
+
PosibbleAuthMap,
|
|
10
|
+
WaitingRequestList } from './helper';
|
|
11
|
+
import { $self, falseValue, noop } from '@alova/shared';
|
|
12
|
+
import {
|
|
13
|
+
callHandlerIfMatchesMeta,
|
|
14
|
+
checkMethodRole,
|
|
15
|
+
defaultLoginMeta,
|
|
16
|
+
defaultLogoutMeta,
|
|
17
|
+
defaultRefreshTokenMeta,
|
|
18
|
+
defaultVisitorMeta,
|
|
19
|
+
refreshTokenIfExpired,
|
|
20
|
+
waitForTokenRefreshed,
|
|
21
|
+
} from './helper';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a server-side token authentication interceptor
|
|
25
|
+
* @returns token authentication interceptor function
|
|
26
|
+
*/
|
|
27
|
+
export function createServerTokenAuthentication<
|
|
28
|
+
SH extends StatesHook<any>,
|
|
29
|
+
RA extends
|
|
30
|
+
| AlovaRequestAdapter<any, any, any>
|
|
31
|
+
| ((...args: any[]) => AlovaRequestAdapter<any, any, any>) = FetchRequestAdapter,
|
|
32
|
+
>({
|
|
33
|
+
visitorMeta,
|
|
34
|
+
login,
|
|
35
|
+
logout,
|
|
36
|
+
refreshTokenOnSuccess,
|
|
37
|
+
refreshTokenOnError,
|
|
38
|
+
assignToken = noop,
|
|
39
|
+
}: ServerTokenAuthenticationOptions<AlovaRequestAdapterUnified<RA>>) {
|
|
40
|
+
let tokenRefreshing = falseValue;
|
|
41
|
+
const waitingList: WaitingRequestList = [];
|
|
42
|
+
|
|
43
|
+
const onAuthRequired: BeforeRequestType<SH, AlovaRequestAdapterUnified<RA>> = onBeforeRequest => async (method) => {
|
|
44
|
+
const isVisitorRole = checkMethodRole(method, visitorMeta || defaultVisitorMeta);
|
|
45
|
+
const isLoginRole = checkMethodRole(method, (login as PosibbleAuthMap)?.metaMatches || defaultLoginMeta);
|
|
46
|
+
// Ignored, login, and token refresh requests do not perform token authentication.
|
|
47
|
+
if (
|
|
48
|
+
!isVisitorRole
|
|
49
|
+
&& !isLoginRole
|
|
50
|
+
&& !checkMethodRole(method, (refreshTokenOnSuccess as PosibbleAuthMap)?.metaMatches || defaultRefreshTokenMeta)
|
|
51
|
+
&& !checkMethodRole(method, (refreshTokenOnError as PosibbleAuthMap)?.metaMatches || defaultRefreshTokenMeta)
|
|
52
|
+
) {
|
|
53
|
+
// If the token is being refreshed, wait for the refresh to complete before sending a request.
|
|
54
|
+
if (tokenRefreshing) {
|
|
55
|
+
await waitForTokenRefreshed(method, waitingList);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!isVisitorRole && !isLoginRole) {
|
|
59
|
+
await assignToken(method);
|
|
60
|
+
}
|
|
61
|
+
return onBeforeRequest?.(method);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const onResponseRefreshToken = () => {
|
|
65
|
+
return {
|
|
66
|
+
onSuccess: async (response: any, method: Method, stop: any) => {
|
|
67
|
+
if (
|
|
68
|
+
!checkMethodRole(method, visitorMeta || defaultVisitorMeta)
|
|
69
|
+
&& !checkMethodRole(method, (login as PosibbleAuthMap)?.metaMatches || defaultLoginMeta)
|
|
70
|
+
&& !checkMethodRole(method, (refreshTokenOnSuccess as PosibbleAuthMap)?.metaMatches || defaultRefreshTokenMeta)
|
|
71
|
+
) {
|
|
72
|
+
const dataResent = await refreshTokenIfExpired(
|
|
73
|
+
method,
|
|
74
|
+
waitingList,
|
|
75
|
+
(refreshing) => {
|
|
76
|
+
tokenRefreshing = refreshing;
|
|
77
|
+
},
|
|
78
|
+
[response, method],
|
|
79
|
+
refreshTokenOnSuccess,
|
|
80
|
+
tokenRefreshing,
|
|
81
|
+
);
|
|
82
|
+
if (dataResent) {
|
|
83
|
+
stop();
|
|
84
|
+
return dataResent;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await callHandlerIfMatchesMeta(method, login, defaultLoginMeta, response);
|
|
89
|
+
await callHandlerIfMatchesMeta(method, logout, defaultLogoutMeta, response);
|
|
90
|
+
return $self(response);
|
|
91
|
+
},
|
|
92
|
+
onError: async (error: any, method: Method) => {
|
|
93
|
+
if (
|
|
94
|
+
!checkMethodRole(method, visitorMeta || defaultVisitorMeta)
|
|
95
|
+
&& !checkMethodRole(method, (login as PosibbleAuthMap)?.metaMatches || defaultLoginMeta)
|
|
96
|
+
&& !checkMethodRole(method, (refreshTokenOnError as PosibbleAuthMap)?.metaMatches || defaultRefreshTokenMeta)
|
|
97
|
+
) {
|
|
98
|
+
const dataResent = await refreshTokenIfExpired(
|
|
99
|
+
method,
|
|
100
|
+
waitingList,
|
|
101
|
+
(refreshing) => {
|
|
102
|
+
tokenRefreshing = refreshing;
|
|
103
|
+
},
|
|
104
|
+
[error, method],
|
|
105
|
+
refreshTokenOnError,
|
|
106
|
+
tokenRefreshing,
|
|
107
|
+
);
|
|
108
|
+
if (dataResent) {
|
|
109
|
+
return dataResent;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return noop;
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
waitingList,
|
|
119
|
+
onAuthRequired,
|
|
120
|
+
onResponseRefreshToken,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import type { axiosRequestAdapter } from '@alova/adapter-axios';
|
|
1
|
+
import type { AxiosRequestAdapter, axiosRequestAdapter } from '@alova/adapter-axios';
|
|
2
|
+
import type { AlovaRequestAdapterUnified, ServerTokenAuthenticationOptions } from 'alova/client';
|
|
2
3
|
import type VueHook from 'alova/vue';
|
|
3
4
|
import type { AxiosResponse } from 'axios';
|
|
4
5
|
import type { RequestResult } from './interface';
|
|
5
|
-
import { createServerTokenAuthentication } from 'alova/client';
|
|
6
6
|
import { postAuthTokenRefresh } from '@/features/api/modules/auth';
|
|
7
7
|
import { useUserStore } from '@/features/stores';
|
|
8
|
+
import { createServerTokenAuthentication } from './alova/token';
|
|
8
9
|
import { RESPONSE_CODE } from './enum';
|
|
9
10
|
|
|
10
|
-
export function createTokenAuthentication(
|
|
11
|
+
export function createTokenAuthentication(
|
|
12
|
+
options: ServerTokenAuthenticationOptions<AlovaRequestAdapterUnified<AxiosRequestAdapter>> = {},
|
|
13
|
+
) {
|
|
11
14
|
return createServerTokenAuthentication<typeof VueHook, typeof axiosRequestAdapter>({
|
|
12
15
|
/**
|
|
13
16
|
* 给权限接口添加Authorization
|
|
@@ -51,5 +54,6 @@ export function createTokenAuthentication() {
|
|
|
51
54
|
userStore.setToken(accessToken, refreshToken);
|
|
52
55
|
},
|
|
53
56
|
},
|
|
57
|
+
...options,
|
|
54
58
|
});
|
|
55
59
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ModuleOptions } from '@/core';
|
|
2
|
-
|
|
2
|
+
import { createTokenAuthentication } from './helper';
|
|
3
3
|
import { createRouterGuard } from './router';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -8,21 +8,7 @@ import { createRouterGuard } from './router';
|
|
|
8
8
|
* 双 token 校验,确认用户身份,注册修改密码和切换组织页面
|
|
9
9
|
*/
|
|
10
10
|
export function Authentication(): ModuleOptions {
|
|
11
|
-
|
|
12
|
-
// Token 过期时的请求,在刷新完 Token 后虽然会重新请求,但是过期请求还是会继续抛出错误。
|
|
13
|
-
// 若是用如下形式注册,则没有问题:
|
|
14
|
-
// const alova = createAlova({
|
|
15
|
-
// beforeRequest: onAuthRequired(method => {
|
|
16
|
-
// }),
|
|
17
|
-
// responded: onResponseRefreshToken({
|
|
18
|
-
// onSuccess(response, method) {
|
|
19
|
-
// },
|
|
20
|
-
// onError(error, method) {
|
|
21
|
-
// },
|
|
22
|
-
// })
|
|
23
|
-
// });
|
|
24
|
-
|
|
25
|
-
// const { onAuthRequired, onResponseRefreshToken } = createTokenAuthentication();
|
|
11
|
+
const { onAuthRequired, onResponseRefreshToken } = createTokenAuthentication();
|
|
26
12
|
|
|
27
13
|
return {
|
|
28
14
|
name: 'built-in:authentication',
|
|
@@ -30,10 +16,10 @@ export function Authentication(): ModuleOptions {
|
|
|
30
16
|
const { router } = ctx;
|
|
31
17
|
createRouterGuard(router);
|
|
32
18
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
hooks: {
|
|
20
|
+
'http:request': onAuthRequired(),
|
|
21
|
+
'http:response': onResponseRefreshToken(),
|
|
22
|
+
},
|
|
37
23
|
};
|
|
38
24
|
}
|
|
39
25
|
|
package/src/built-in/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ defineOptions({
|
|
|
12
12
|
name: 'TopMode',
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
const { settingsStore, menuStore,
|
|
15
|
+
const { settingsStore, menuStore, generateTitle } = useContext();
|
|
16
16
|
const { switchTo } = useMenu();
|
|
17
17
|
|
|
18
18
|
function iconName(isActive: boolean, icon?: string, activeIcon?: string) {
|
|
@@ -113,7 +113,7 @@ onMounted(() => {
|
|
|
113
113
|
'text-[var(--g-header-menu-active-color)]! bg-[var(--g-header-menu-active-bg)]!': index === menuStore.actived,
|
|
114
114
|
'rounded-2': settingsStore.settings.menu.isRounded,
|
|
115
115
|
}"
|
|
116
|
-
:title="
|
|
116
|
+
:title="generateTitle(item.meta?.title)"
|
|
117
117
|
@click="switchTo(index, item)"
|
|
118
118
|
>
|
|
119
119
|
<div class="flex flex-row flex-1 items-center justify-center px-8px">
|
|
@@ -124,7 +124,7 @@ onMounted(() => {
|
|
|
124
124
|
class="menu-item-container-icon transition-transform group-hover:scale-120 mr-5px"
|
|
125
125
|
/>
|
|
126
126
|
<span class="w-full flex-1 truncate text-center text-sm transition-height transition-opacity transition-width">
|
|
127
|
-
{{
|
|
127
|
+
{{ generateTitle(item.meta?.title) }}
|
|
128
128
|
</span>
|
|
129
129
|
</div>
|
|
130
130
|
</div>
|
|
@@ -18,7 +18,7 @@ const props = withDefaults(
|
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
const rootMenu = inject(rootMenuInjectionKey)!;
|
|
21
|
-
const {
|
|
21
|
+
const { generateTitle } = useContext();
|
|
22
22
|
|
|
23
23
|
const itemRef = ref<HTMLElement>();
|
|
24
24
|
|
|
@@ -78,7 +78,7 @@ defineExpose({
|
|
|
78
78
|
'px-2!': rootMenu.isMenuPopup && level === 0,
|
|
79
79
|
'py-3!': rootMenu.props.rounded && rootMenu.isMenuPopup && level !== 0,
|
|
80
80
|
}"
|
|
81
|
-
:title="
|
|
81
|
+
:title="generateTitle(item.meta?.title)"
|
|
82
82
|
>
|
|
83
83
|
<div
|
|
84
84
|
class="menu-item-container-layout inline-flex flex-1 items-center justify-center gap-[12px]"
|
|
@@ -106,7 +106,7 @@ defineExpose({
|
|
|
106
106
|
'system-point': icon === 'system-point' && isItemActive,
|
|
107
107
|
}"
|
|
108
108
|
>
|
|
109
|
-
{{
|
|
109
|
+
{{ generateTitle(item.meta?.title) }}
|
|
110
110
|
</span>
|
|
111
111
|
<HBadge
|
|
112
112
|
v-if="item.meta?.badge"
|
|
@@ -11,7 +11,7 @@ defineOptions({
|
|
|
11
11
|
|
|
12
12
|
const route = useRoute();
|
|
13
13
|
|
|
14
|
-
const { settingsStore, menuStore,
|
|
14
|
+
const { settingsStore, menuStore, generateTitle } = useContext();
|
|
15
15
|
const { switchTo } = useMenu();
|
|
16
16
|
|
|
17
17
|
function iconName(isActive: boolean, icon?: string, activeIcon?: string) {
|
|
@@ -50,7 +50,7 @@ function iconName(isActive: boolean, icon?: string, activeIcon?: string) {
|
|
|
50
50
|
'text-[var(--g-main-sidebar-menu-active-color)]! bg-[var(--g-main-sidebar-menu-active-bg)]!': index === menuStore.actived,
|
|
51
51
|
'rounded-2': settingsStore.settings.menu.isRounded,
|
|
52
52
|
}"
|
|
53
|
-
:title="
|
|
53
|
+
:title="generateTitle(item.meta?.title)"
|
|
54
54
|
@click="switchTo(index, item)"
|
|
55
55
|
>
|
|
56
56
|
<div class="w-full inline-flex flex-1 flex-col items-center justify-center gap-[2px]">
|
|
@@ -62,7 +62,7 @@ function iconName(isActive: boolean, icon?: string, activeIcon?: string) {
|
|
|
62
62
|
/>
|
|
63
63
|
<!-- 不需要title? -->
|
|
64
64
|
<!-- <span class="w-full flex-1 truncate text-center text-sm transition-height transition-opacity transition-width">
|
|
65
|
-
{{
|
|
65
|
+
{{ generateTitle(item.meta?.title) }}
|
|
66
66
|
</span> -->
|
|
67
67
|
</div>
|
|
68
68
|
</div>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { useI18n } from 'vue-i18n';
|
|
3
2
|
import IconamoonSearch from '~icons/iconamoon/search';
|
|
4
3
|
import { useContext } from '../../composables/useContext';
|
|
5
4
|
import { useGlobalSearch } from '../../composables/useGlobalSearch';
|
|
@@ -10,7 +9,6 @@ defineOptions({
|
|
|
10
9
|
|
|
11
10
|
const { settingsStore } = useContext();
|
|
12
11
|
const { toggle: toggleSearch } = useGlobalSearch();
|
|
13
|
-
const { t } = useI18n();
|
|
14
12
|
|
|
15
13
|
const searchComponentsClass = computed(() => {
|
|
16
14
|
const componentsClass = {
|
|
@@ -33,7 +31,7 @@ const searchComponentsClass = computed(() => {
|
|
|
33
31
|
@click="toggleSearch('menu')"
|
|
34
32
|
>
|
|
35
33
|
<IconamoonSearch text="14px" />
|
|
36
|
-
<span class="text-sm transition ">{{
|
|
34
|
+
<span class="text-sm transition ">{{ '搜索' }}</span>
|
|
37
35
|
<HKbd
|
|
38
36
|
v-if="settingsStore.settings.navSearch.enableHotkeys"
|
|
39
37
|
class="ml-2"
|
|
@@ -6,7 +6,6 @@ import { Dialog, DialogDescription, DialogPanel, TransitionChild, TransitionRoot
|
|
|
6
6
|
import hotkeys from 'hotkeys-js';
|
|
7
7
|
import { cloneDeep } from 'lodash-es';
|
|
8
8
|
import { OverlayScrollbarsComponent } from 'overlayscrollbars-vue';
|
|
9
|
-
import { useI18n } from 'vue-i18n';
|
|
10
9
|
import AntDesignCaretDownFilled from '~icons/ant-design/caret-down-filled';
|
|
11
10
|
import AntDesignCaretUpFilled from '~icons/ant-design/caret-up-filled';
|
|
12
11
|
import EpSearch from '~icons/ep/search';
|
|
@@ -45,17 +44,14 @@ const transitionClass = computed(() => {
|
|
|
45
44
|
};
|
|
46
45
|
});
|
|
47
46
|
|
|
48
|
-
const { t } = useI18n();
|
|
49
|
-
|
|
50
47
|
const router = useRouter();
|
|
51
|
-
const { settingsStore, routeStore, tabbarStore,
|
|
48
|
+
const { settingsStore, routeStore, tabbarStore, generateTitle } = useContext();
|
|
52
49
|
|
|
53
50
|
type searchTypes = 'menu' | 'tab';
|
|
54
51
|
interface listTypes {
|
|
55
52
|
path: string
|
|
56
53
|
icon?: string
|
|
57
54
|
title?: string | (() => string)
|
|
58
|
-
i18n?: string
|
|
59
55
|
link?: string
|
|
60
56
|
breadcrumb: any[]
|
|
61
57
|
}
|
|
@@ -76,13 +72,13 @@ const resultList = computed(() => {
|
|
|
76
72
|
let result = [];
|
|
77
73
|
result = sourceList.value.filter((item) => {
|
|
78
74
|
let flag = false;
|
|
79
|
-
if (
|
|
75
|
+
if (generateTitle(item.title).toString().includes(searchInput.value)) {
|
|
80
76
|
flag = true;
|
|
81
77
|
}
|
|
82
78
|
if (item.path.includes(searchInput.value)) {
|
|
83
79
|
flag = true;
|
|
84
80
|
}
|
|
85
|
-
if (item.breadcrumb.some((b: {
|
|
81
|
+
if (item.breadcrumb.some((b: { title: any }) => generateTitle(b.title).toString().includes(searchInput.value))) {
|
|
86
82
|
flag = true;
|
|
87
83
|
}
|
|
88
84
|
return flag;
|
|
@@ -155,27 +151,24 @@ function hasChildren(item: RouteRecordRaw) {
|
|
|
155
151
|
}
|
|
156
152
|
return flag;
|
|
157
153
|
}
|
|
158
|
-
function getSourceList(arr: RouteRecordRaw[], basePath?: string, icon?: string, breadcrumb?: { title?: string | (() => string)
|
|
154
|
+
function getSourceList(arr: RouteRecordRaw[], basePath?: string, icon?: string, breadcrumb?: { title?: string | (() => string) }[]) {
|
|
159
155
|
arr.forEach((item) => {
|
|
160
156
|
if (item.meta?.sidebar !== false) {
|
|
161
157
|
const breadcrumbTemp = cloneDeep(breadcrumb) || [];
|
|
162
158
|
if (item.children && hasChildren(item)) {
|
|
163
159
|
breadcrumbTemp.push({
|
|
164
160
|
title: item.meta?.title,
|
|
165
|
-
i18n: item.meta?.i18n,
|
|
166
161
|
});
|
|
167
162
|
getSourceList(item.children, resolveRoutePath(basePath, item.path), item.meta?.icon ?? icon, breadcrumbTemp);
|
|
168
163
|
}
|
|
169
164
|
else {
|
|
170
165
|
breadcrumbTemp.push({
|
|
171
166
|
title: item.meta?.title,
|
|
172
|
-
i18n: item.meta?.i18n,
|
|
173
167
|
});
|
|
174
168
|
sourceList.value.push({
|
|
175
169
|
path: resolveRoutePath(basePath, item.path),
|
|
176
170
|
icon: item.meta?.icon ?? icon,
|
|
177
171
|
title: item.meta?.title,
|
|
178
|
-
i18n: item.meta?.i18n,
|
|
179
172
|
link: item.meta?.link,
|
|
180
173
|
breadcrumb: breadcrumbTemp,
|
|
181
174
|
});
|
|
@@ -190,7 +183,6 @@ function getSourceListByTabs(arr: Tabbar.recordRaw[]) {
|
|
|
190
183
|
icon: item.icon,
|
|
191
184
|
title: item.title,
|
|
192
185
|
path: item.fullPath,
|
|
193
|
-
i18n: item.i18n,
|
|
194
186
|
breadcrumb: [],
|
|
195
187
|
});
|
|
196
188
|
});
|
|
@@ -285,8 +277,8 @@ defineExpose({
|
|
|
285
277
|
v-if="settingsStore.settings.tabbar.enable"
|
|
286
278
|
v-model="searchType"
|
|
287
279
|
:options="[
|
|
288
|
-
{ label:
|
|
289
|
-
{ label:
|
|
280
|
+
{ label: '搜索导航菜单', value: 'menu' },
|
|
281
|
+
{ label: '搜索标签页', value: 'tab' },
|
|
290
282
|
]"
|
|
291
283
|
class="mb-4 flex!"
|
|
292
284
|
@click.stop
|
|
@@ -298,7 +290,7 @@ defineExpose({
|
|
|
298
290
|
<input
|
|
299
291
|
ref="searchInputRef"
|
|
300
292
|
v-model="searchInput"
|
|
301
|
-
|
|
293
|
+
placeholder="搜索页面,支持标题、URL模糊查询"
|
|
302
294
|
class="w-full border-0 rounded-md bg-transparent px-3 text-base text-dark dark:text-white focus:outline-none placeholder-stone-4 dark:placeholder-stone-5"
|
|
303
295
|
@keydown.esc="toggle()"
|
|
304
296
|
@keydown.up.prevent="keyUp"
|
|
@@ -332,10 +324,10 @@ defineExpose({
|
|
|
332
324
|
:class="{ 'scale-120 text-ui-primary': index === actived }"
|
|
333
325
|
/>
|
|
334
326
|
<div class="flex flex-1 flex-col gap-1 truncate px-4 py-3" border-l="~ solid stone-2 dark:stone-7">
|
|
335
|
-
<div class="truncate text-base font-bold">{{
|
|
327
|
+
<div class="truncate text-base font-bold">{{ generateTitle(item.title) }}</div>
|
|
336
328
|
<Breadcrumb v-if="item.breadcrumb.length" class="truncate">
|
|
337
329
|
<BreadcrumbItem v-for="(bc, bcIndex) in item.breadcrumb" :key="bcIndex" class="text-xs">
|
|
338
|
-
{{
|
|
330
|
+
{{ generateTitle(bc.title) }}
|
|
339
331
|
</BreadcrumbItem>
|
|
340
332
|
</Breadcrumb>
|
|
341
333
|
</div>
|
|
@@ -357,7 +349,7 @@ defineExpose({
|
|
|
357
349
|
<HKbd>
|
|
358
350
|
<IonMdReturnLeft text="14px" />
|
|
359
351
|
</HKbd>
|
|
360
|
-
<span>{{
|
|
352
|
+
<span>{{ '访问' }}</span>
|
|
361
353
|
</div>
|
|
362
354
|
<div class="inline-flex items-center gap-1 text-xs">
|
|
363
355
|
<HKbd>
|
|
@@ -366,14 +358,14 @@ defineExpose({
|
|
|
366
358
|
<HKbd>
|
|
367
359
|
<AntDesignCaretDownFilled text="14px" />
|
|
368
360
|
</HKbd>
|
|
369
|
-
<span>{{
|
|
361
|
+
<span>{{ '切换' }}</span>
|
|
370
362
|
</div>
|
|
371
363
|
</div>
|
|
372
364
|
<div v-if="settingsStore.settings.navSearch.enableHotkeys" class="inline-flex items-center gap-1 text-xs">
|
|
373
365
|
<HKbd>
|
|
374
366
|
ESC
|
|
375
367
|
</HKbd>
|
|
376
|
-
<span>{{
|
|
368
|
+
<span>{{ '退出' }}</span>
|
|
377
369
|
</div>
|
|
378
370
|
</div>
|
|
379
371
|
</div>
|