@pubinfo/core 2.0.0-rc.2 → 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-D2RJrc9O.js → AppSetting-BI-oNc4e.js} +19 -19
- package/dist/{HCheckList.vue_vue_type_script_setup_true_lang-DusVz35O.js → HCheckList.vue_vue_type_script_setup_true_lang-BdLpkcoh.js} +1 -1
- package/dist/{HToggle-DMcVgMVY.js → HToggle-DxdWLgp-.js} +1 -1
- package/dist/{PreferencesContent-Dtd9rtew.js → PreferencesContent-CCYkZeCT.js} +52 -52
- package/dist/{SettingBreadcrumb-QSCSviKM.js → SettingBreadcrumb-BTyfiy4k.js} +5 -5
- package/dist/{SettingCopyright-Dr5P6yfq.js → SettingCopyright-g6UHi8pZ.js} +2 -2
- package/dist/{SettingEnableTransition-DGiHEbCI.js → SettingEnableTransition-Ci-5bhbR.js} +12 -12
- package/dist/{SettingHome-CEPcBlds.js → SettingHome-K4Iel0Hr.js} +8 -8
- package/dist/{SettingMenu-BJdjnRA6.js → SettingMenu-BYLWzA5i.js} +14 -14
- package/dist/{SettingMode-BnuCHoEY.js → SettingMode-tRisyKtg.js} +3 -3
- package/dist/{SettingNavSearch-CiU4BmlU.js → SettingNavSearch-CSM6mPf8.js} +6 -6
- package/dist/{SettingOther-DTHjVlFe.js → SettingOther-Bj5KF_vC.js} +11 -11
- package/dist/{SettingPage-D75_Nf05.js → SettingPage-CFjmrVI7.js} +2 -2
- package/dist/{SettingTabbar-D48dzvgA.js → SettingTabbar-uFYiaZhK.js} +13 -13
- package/dist/{SettingThemes-D-8vTs5n.js → SettingThemes-C-tMq9o5.js} +12 -12
- package/dist/{SettingToolbar-DjIjm9V-.js → SettingToolbar-BfDzijNU.js} +10 -10
- package/dist/{SettingTopbar-Cg30OTH3.js → SettingTopbar-DTDv4NXD.js} +6 -6
- package/dist/{SettingWidthMode-BKV_7kb8.js → SettingWidthMode-PkiwrHe3.js} +11 -11
- package/dist/{TopThinMode-JFYsp_lJ.js → TopThinMode-BrvA8pV0.js} +3 -3
- 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/Layout.vue.d.ts +1 -0
- package/dist/built-in/layout-component/components/Tools/Fullscreen.vue.d.ts +2 -0
- package/dist/built-in/layout-component/components/Tools/PageReload.vue.d.ts +2 -0
- package/dist/built-in/layout-component/components/Tools/SearchBar.vue.d.ts +2 -0
- package/dist/built-in/layout-component/components/Tools/index.vue.d.ts +47 -1
- package/dist/built-in/layout-component/components/Tools/interface.d.ts +26 -0
- package/dist/built-in/layout-component/components/ui/HDropdownMenu.vue.d.ts +2 -7
- 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/useHotkey.d.ts +1 -5
- package/dist/built-in/layout-component/composables/useMainPage.d.ts +1 -1
- package/dist/built-in/layout-component/composables/useMenu.d.ts +1 -1
- package/dist/built-in/layout-component/composables/useTabbar.d.ts +1 -1
- package/dist/built-in/layout-component/composables/useTitle.d.ts +3 -2
- package/dist/built-in/layout-component/composables/useWatermark.d.ts +3 -1
- package/dist/built-in/layout-component/index.d.ts +9 -6
- package/dist/built-in/layout-component/utils/index.d.ts +0 -1
- package/dist/{colors-CODcBxrF.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-RT-QBzm0.js → index-BSevJVD5.js} +10 -15
- package/dist/{index-BVLkBCRY.js → index-BfGqLWFB.js} +6418 -6439
- package/dist/{index-DQn1WFMa.js → index-CYoFRwvw.js} +2 -2
- package/dist/{index-DmcblkoZ.js → index-ConeY38N.js} +13 -13
- package/dist/{index-BAoB7aoj.js → index-DV3hkzKA.js} +1 -1
- package/dist/{index-D4_xmL_A.js → index-Ddw98rJ5.js} +25 -25
- package/dist/{index-DvJr0paY.js → index-DrC787X_.js} +2 -2
- package/dist/{index-BROqFYXS.js → index-Dv9ndBoi.js} +1 -1
- package/dist/{index-Jd3PYkpj.js → index-IAYhIBQH.js} +16815 -16694
- package/dist/index.d.ts +4 -2
- package/dist/index.js +53 -55
- package/dist/{pick-BLJM77QN.js → pick-vpv9EEvu.js} +1 -1
- package/dist/style.css +1 -1
- package/package.json +11 -11
- 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/Layout.vue +11 -22
- package/src/built-in/layout-component/components/Header/TopMode/index.vue +4 -4
- package/src/built-in/layout-component/components/Menu/item.vue +3 -3
- package/src/built-in/layout-component/components/Sidebar/MainSidebar.vue +4 -4
- package/src/built-in/layout-component/components/Sidebar/index.vue +1 -1
- package/src/built-in/layout-component/components/Tools/DarkModeToggle.vue +108 -0
- package/src/built-in/layout-component/components/Tools/Fullscreen.vue +24 -0
- package/src/built-in/layout-component/components/Tools/PageReload.vue +22 -0
- package/src/built-in/layout-component/components/Tools/SearchBar.vue +42 -0
- package/src/built-in/layout-component/components/Tools/{Search.vue → SearchPanel.vue} +13 -21
- package/src/built-in/layout-component/components/Tools/index.vue +71 -142
- package/src/built-in/layout-component/components/Tools/interface.ts +27 -0
- package/src/built-in/layout-component/components/Topbar/Tabbar/MoreAction.vue +9 -12
- package/src/built-in/layout-component/components/Topbar/Tabbar/index.vue +12 -15
- 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/components/ui/HDropdownMenu.vue +19 -26
- package/src/built-in/layout-component/composables/useContext.ts +1 -1
- package/src/built-in/layout-component/composables/useGetComputedStyle.ts +2 -3
- package/src/built-in/layout-component/composables/useHotkey.ts +6 -10
- package/src/built-in/layout-component/composables/useMainPage.ts +5 -6
- package/src/built-in/layout-component/composables/useMenu.ts +3 -5
- package/src/built-in/layout-component/composables/useTabbar.ts +3 -5
- package/src/built-in/layout-component/composables/useTitle.ts +10 -17
- package/src/built-in/layout-component/composables/useWatermark.ts +25 -12
- package/src/built-in/layout-component/index.ts +21 -12
- package/src/built-in/layout-component/provider.ts +7 -3
- package/src/built-in/layout-component/utils/index.ts +0 -1
- package/src/built-in/settings/router.ts +5 -1
- 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 +38 -4
- package/src/index.ts +7 -11
- package/types/vue-router.d.ts +0 -3
- package/dist/built-in/layout-component/utils/eventBus.d.ts +0 -5
- 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/layout-component/components/Tools/DayNightSwitch.vue +0 -70
- package/src/built-in/layout-component/utils/eventBus.ts +0 -8
- 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/dist/built-in/layout-component/components/Tools/{DayNightSwitch.vue.d.ts → DarkModeToggle.vue.d.ts} +0 -0
- /package/dist/built-in/layout-component/components/Tools/{Search.vue.d.ts → SearchPanel.vue.d.ts} +0 -0
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,17 +22,17 @@
|
|
|
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",
|
|
35
|
-
"@pubinfo/pro-components": "^1.7.
|
|
35
|
+
"@pubinfo/pro-components": "^1.7.3",
|
|
36
36
|
"@unocss/reset": "^66.4.2",
|
|
37
37
|
"@vueuse/core": "^13.7.0",
|
|
38
38
|
"@vueuse/integrations": "^13.5.0",
|
|
@@ -58,10 +58,11 @@
|
|
|
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
|
-
"@pubinfo/openapi": "^0.
|
|
64
|
-
"@pubinfo/preset-openapi": "^0.
|
|
64
|
+
"@pubinfo/openapi": "^0.9.0",
|
|
65
|
+
"@pubinfo/preset-openapi": "^0.9.0",
|
|
65
66
|
"@types/lodash-es": "^4.17.12",
|
|
66
67
|
"@types/md5": "^2.3.5",
|
|
67
68
|
"@types/nprogress": "^0.2.3",
|
|
@@ -80,13 +81,12 @@
|
|
|
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
|
-
"dev": "vite build -w -m watch
|
|
89
|
+
"dev": "vite build -w -m watch",
|
|
90
90
|
"build": "vite build",
|
|
91
91
|
"openapi": "pnpx @pubinfo/openapi generate"
|
|
92
92
|
}
|
|
@@ -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
|
@@ -4,30 +4,23 @@ import BackTop from './components/BackTop/index.vue';
|
|
|
4
4
|
import LayoutContent from './components/Content/index.vue';
|
|
5
5
|
import Copyright from './components/Copyright/index.vue';
|
|
6
6
|
import LayoutHeader from './components/Header/index.vue';
|
|
7
|
-
|
|
8
7
|
import SettingBar from './components/SettingBar/index.vue';
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
import Topbar from './components/Topbar/index.vue';
|
|
8
|
+
import LayoutSidebar from './components/Sidebar/index.vue';
|
|
9
|
+
import LayoutTopbar from './components/Topbar/index.vue';
|
|
10
|
+
|
|
13
11
|
import { useContext } from './composables/useContext';
|
|
14
12
|
import { useGetSidebarActualWidth } from './composables/useGetComputedStyle';
|
|
15
13
|
import { useHotkey } from './composables/useHotkey';
|
|
16
|
-
import useWatermark from './composables/useWatermark';
|
|
14
|
+
import { useWatermark } from './composables/useWatermark';
|
|
17
15
|
|
|
18
16
|
defineOptions({
|
|
19
17
|
name: 'Layout',
|
|
20
18
|
});
|
|
21
19
|
|
|
22
20
|
const routeInfo = useRoute();
|
|
23
|
-
const { settingsStore
|
|
21
|
+
const { settingsStore } = useContext();
|
|
24
22
|
const { mainSidebarActualWidth, subSidebarActualWidth } = useGetSidebarActualWidth();
|
|
25
|
-
|
|
26
|
-
useHotkey({
|
|
27
|
-
settingsStore,
|
|
28
|
-
menuStore,
|
|
29
|
-
});
|
|
30
|
-
|
|
23
|
+
useHotkey();
|
|
31
24
|
useWatermark();
|
|
32
25
|
|
|
33
26
|
watch(() => settingsStore.settings.menu.subMenuCollapse, (val) => {
|
|
@@ -68,13 +61,9 @@ watch(() => routeInfo.path, () => {
|
|
|
68
61
|
class="sidebar-container"
|
|
69
62
|
:class="{ show: settingsStore.mode === 'mobile' && !settingsStore.settings.menu.subMenuCollapse }"
|
|
70
63
|
>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
</slot> -->
|
|
75
|
-
|
|
76
|
-
<MainSidebar />
|
|
77
|
-
<SubSidebar />
|
|
64
|
+
<slot name="sidebar">
|
|
65
|
+
<LayoutSidebar />
|
|
66
|
+
</slot>
|
|
78
67
|
</div>
|
|
79
68
|
|
|
80
69
|
<div
|
|
@@ -85,7 +74,7 @@ watch(() => routeInfo.path, () => {
|
|
|
85
74
|
|
|
86
75
|
<div class="main-container" :style="{ 'padding-bottom': routeInfo.meta.paddingBottom }">
|
|
87
76
|
<slot name="topbar">
|
|
88
|
-
<
|
|
77
|
+
<LayoutTopbar />
|
|
89
78
|
</slot>
|
|
90
79
|
|
|
91
80
|
<div class="main">
|
|
@@ -213,7 +202,7 @@ watch(() => routeInfo.path, () => {
|
|
|
213
202
|
--at-apply: visible op-100;
|
|
214
203
|
}
|
|
215
204
|
|
|
216
|
-
.wrapper .main-sidebar-container:not(.main-sidebar-leave-active) + .sub-sidebar-container {
|
|
205
|
+
.wrapper ::v-deep(.main-sidebar-container:not(.main-sidebar-leave-active) + .sub-sidebar-container) {
|
|
217
206
|
--at-apply: left-[--g-main-sidebar-width];
|
|
218
207
|
}
|
|
219
208
|
|
|
@@ -4,7 +4,7 @@ import { useElementSize } from '@vueuse/core';
|
|
|
4
4
|
import SolarWidget5BoldDuotone from '~icons/solar/widget-5-bold-duotone';
|
|
5
5
|
import { PubinfoIcon } from '@/features/components';
|
|
6
6
|
import { useContext } from '../../../composables/useContext';
|
|
7
|
-
import useMenu from '../../../composables/useMenu';
|
|
7
|
+
import { useMenu } from '../../../composables/useMenu';
|
|
8
8
|
import More from './More.vue';
|
|
9
9
|
import NotCursor from './NotCursor.vue';
|
|
10
10
|
|
|
@@ -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"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { PubinfoIcon } from '@/features/components';
|
|
3
3
|
import { useContext } from '../../composables/useContext';
|
|
4
|
-
import useMenu from '../../composables/useMenu';
|
|
4
|
+
import { useMenu } from '../../composables/useMenu';
|
|
5
5
|
import Logo from '../Logo/index.vue';
|
|
6
6
|
import Menu from '../Menu/index.vue';
|
|
7
7
|
|
|
@@ -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>
|