feat: 对接登录和命名空间规范接口
This commit is contained in:
parent
31bf70fa73
commit
6b1cf17428
@ -1,5 +1,5 @@
|
||||
# backend service base url, test environment
|
||||
VITE_SERVICE_BASE_URL=https://mock.apifox.com/m1/3109515-0-default
|
||||
VITE_SERVICE_BASE_URL=http://preview.easyretry.com/easy-retry
|
||||
|
||||
# other backend service base url, test environment
|
||||
VITE_OTHER_SERVICE_BASE_URL= `{
|
||||
|
@ -29,6 +29,7 @@ function createProxyItem(item: App.Service.ServiceConfigItem) {
|
||||
proxy[item.proxyPattern] = {
|
||||
target: item.baseURL,
|
||||
changeOrigin: true,
|
||||
ws: false,
|
||||
rewrite: path => path.replace(new RegExp(`^${item.proxyPattern}`), '')
|
||||
};
|
||||
|
||||
|
@ -58,6 +58,7 @@
|
||||
"naive-ui": "2.38.1",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "2.1.7",
|
||||
"ts-md5": "^1.3.1",
|
||||
"vue": "3.4.21",
|
||||
"vue-draggable-plus": "0.3.5",
|
||||
"vue-i18n": "9.10.2",
|
||||
|
@ -53,6 +53,9 @@ importers:
|
||||
pinia:
|
||||
specifier: 2.1.7
|
||||
version: 2.1.7(typescript@5.4.2)(vue@3.4.21)
|
||||
ts-md5:
|
||||
specifier: ^1.3.1
|
||||
version: 1.3.1
|
||||
vue:
|
||||
specifier: 3.4.21
|
||||
version: 3.4.21(typescript@5.4.2)
|
||||
@ -7330,6 +7333,11 @@ packages:
|
||||
typescript: 5.4.2
|
||||
dev: true
|
||||
|
||||
/ts-md5@1.3.1:
|
||||
resolution: {integrity: sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/tslib@2.3.0:
|
||||
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||
dev: false
|
||||
|
@ -82,7 +82,7 @@ function handleDropdown(key: DropdownKey) {
|
||||
<div>
|
||||
<ButtonIcon>
|
||||
<SvgIcon icon="ph:user-circle" class="text-icon-large" />
|
||||
<span class="text-16px font-medium">{{ authStore.userInfo.userName }}</span>
|
||||
<span class="text-16px font-medium">{{ authStore.userInfo.username }}</span>
|
||||
</ButtonIcon>
|
||||
</div>
|
||||
</NDropdown>
|
||||
|
@ -8,6 +8,7 @@ import HorizontalMenu from '../global-menu/base-menu.vue';
|
||||
import GlobalLogo from '../global-logo/index.vue';
|
||||
import GlobalBreadcrumb from '../global-breadcrumb/index.vue';
|
||||
import GlobalSearch from '../global-search/index.vue';
|
||||
import NamespaceSelect from '../namespace-select/index.vue';
|
||||
import { useMixMenuContext } from '../../hooks/use-mix-menu';
|
||||
import ThemeButton from './components/theme-button.vue';
|
||||
import UserAvatar from './components/user-avatar.vue';
|
||||
@ -55,6 +56,7 @@ const headerMenus = computed(() => {
|
||||
<GlobalBreadcrumb v-if="!appStore.isMobile" class="ml-12px" />
|
||||
</div>
|
||||
<div class="h-full flex-y-center justify-end">
|
||||
<NamespaceSelect />
|
||||
<GlobalSearch />
|
||||
<FullScreen v-if="!appStore.isMobile" :full="isFullscreen" @click="toggle" />
|
||||
<LangSwitch :lang="appStore.locale" :lang-options="appStore.localeOptions" @change-lang="appStore.changeLocale" />
|
||||
|
37
src/layouts/modules/namespace-select/index.vue
Normal file
37
src/layouts/modules/namespace-select/index.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { localStg } from '@/utils/storage';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const namespaceId = ref<string>(localStg.get('namespaceId')!);
|
||||
|
||||
const userInfo = localStg.get('userInfo');
|
||||
const options = ref(
|
||||
userInfo?.namespaceIds.map(item => {
|
||||
return { label: item.name, value: item.uniqueId };
|
||||
})
|
||||
);
|
||||
|
||||
const onChange = (value: string) => {
|
||||
localStg.set('namespaceId', value);
|
||||
setTimeout(() => {
|
||||
router.go(0);
|
||||
}, 500);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NSelect v-model:value="namespaceId" class="namespace-select" :options="options" @update:value="onChange" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.namespace-select {
|
||||
width: 150px;
|
||||
|
||||
:deep(.n-base-selection) {
|
||||
border-radius: 32px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -30,10 +30,7 @@ export function createPermissionGuard(router: Router) {
|
||||
// 2. if the user is super admin, then it is allowed to access
|
||||
// 3. if the user's role is included in the route's "roles", then it is allowed to access
|
||||
const SUPER_ADMIN = 'R_SUPER';
|
||||
const hasPermission =
|
||||
!routeRoles.length ||
|
||||
authStore.userInfo.roles.includes(SUPER_ADMIN) ||
|
||||
authStore.userInfo.roles.some(role => routeRoles.includes(role));
|
||||
const hasPermission = !routeRoles.length || authStore.userInfo.role === SUPER_ADMIN;
|
||||
|
||||
const strategicPatterns: CommonType.StrategicPattern[] = [
|
||||
// 1. if it is login route when logged in, change to the root page
|
||||
|
@ -3,15 +3,15 @@ import { request } from '../request';
|
||||
/**
|
||||
* Login
|
||||
*
|
||||
* @param userName User name
|
||||
* @param username User name
|
||||
* @param password Password
|
||||
*/
|
||||
export function fetchLogin(userName: string, password: string) {
|
||||
export function fetchLogin(username: string, password: string) {
|
||||
return request<Api.Auth.LoginToken>({
|
||||
url: '/auth/login',
|
||||
method: 'post',
|
||||
data: {
|
||||
userName,
|
||||
username,
|
||||
password
|
||||
}
|
||||
});
|
||||
@ -19,7 +19,7 @@ export function fetchLogin(userName: string, password: string) {
|
||||
|
||||
/** Get user info */
|
||||
export function fetchGetUserInfo() {
|
||||
return request<Api.Auth.UserInfo>({ url: '/auth/getUserInfo' });
|
||||
return request<Api.Auth.UserInfo>({ url: '/user/info' });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,9 +8,7 @@ const { baseURL, otherBaseURL } = getServiceBaseURL(import.meta.env, isHttpProxy
|
||||
export const request = createFlatRequest<App.Service.Response>(
|
||||
{
|
||||
baseURL,
|
||||
headers: {
|
||||
apifoxToken: 'XL299LiMEDZ0H5h3A29PxwQXdMJqWyY2'
|
||||
}
|
||||
timeout: 6000
|
||||
},
|
||||
{
|
||||
async onRequest(config) {
|
||||
@ -18,15 +16,18 @@ export const request = createFlatRequest<App.Service.Response>(
|
||||
|
||||
// set token
|
||||
const token = localStg.get('token');
|
||||
const Authorization = token ? `Bearer ${token}` : null;
|
||||
Object.assign(headers, { Authorization });
|
||||
const namespaceId = localStg.get('namespaceId');
|
||||
// const Authorization = token ? `Bearer ${token}` : null;
|
||||
headers['EASY-RETRY-AUTH'] = token;
|
||||
headers['EASY-RETRY-NAMESPACE-ID'] = namespaceId;
|
||||
Object.assign(headers, { 'EASY-RETRY-AUTH': token, 'EASY-RETRY-NAMESPACE-ID': namespaceId });
|
||||
|
||||
return config;
|
||||
},
|
||||
isBackendSuccess(response) {
|
||||
// when the backend response code is "0000", it means the request is success
|
||||
// you can change this logic by yourself
|
||||
return response.data.code === '0000';
|
||||
return response.data.status === 1;
|
||||
},
|
||||
async onBackendFail(_response) {
|
||||
// when the backend response code is not "0000", it means the request is fail
|
||||
@ -42,7 +43,7 @@ export const request = createFlatRequest<App.Service.Response>(
|
||||
|
||||
// show backend error message
|
||||
if (error.code === BACKEND_ERROR_CODE) {
|
||||
message = error.response?.data?.msg || message;
|
||||
message = error.response?.data?.message || message;
|
||||
}
|
||||
|
||||
window.$message?.error(message);
|
||||
|
@ -58,7 +58,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
||||
if (routeStore.isInitAuthRoute) {
|
||||
window.$notification?.success({
|
||||
title: $t('page.login.common.loginSuccess'),
|
||||
content: $t('page.login.common.welcomeBack', { userName: userInfo.userName }),
|
||||
content: $t('page.login.common.welcomeBack', { userName: userInfo.username }),
|
||||
duration: 4500
|
||||
});
|
||||
}
|
||||
@ -73,7 +73,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
||||
async function loginByToken(loginToken: Api.Auth.LoginToken) {
|
||||
// 1. stored in the localStorage, the later requests need it in headers
|
||||
localStg.set('token', loginToken.token);
|
||||
localStg.set('refreshToken', loginToken.refreshToken);
|
||||
localStg.set('namespaceId', loginToken.namespaceIds[0].uniqueId);
|
||||
|
||||
const { data: info, error } = await fetchGetUserInfo();
|
||||
|
||||
|
@ -8,9 +8,11 @@ export function getToken() {
|
||||
/** Get user info */
|
||||
export function getUserInfo() {
|
||||
const emptyInfo: Api.Auth.UserInfo = {
|
||||
userId: '',
|
||||
userName: '',
|
||||
roles: []
|
||||
id: '',
|
||||
mode: '',
|
||||
username: '',
|
||||
role: '',
|
||||
namespaceIds: []
|
||||
};
|
||||
const userInfo = localStg.get('userInfo') || emptyInfo;
|
||||
|
||||
@ -20,6 +22,6 @@ export function getUserInfo() {
|
||||
/** Clear auth storage */
|
||||
export function clearAuthStorage() {
|
||||
localStg.remove('token');
|
||||
localStg.remove('refreshToken');
|
||||
localStg.remove('namespaceId');
|
||||
localStg.remove('userInfo');
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { ROOT_ROUTE, createRoutes, getAuthVueRoutes } from '@/router/routes';
|
||||
import { getRouteName, getRoutePath } from '@/router/elegant/transform';
|
||||
import { fetchGetUserRoutes, fetchIsRouteExist } from '@/service/api';
|
||||
import { useAppStore } from '../app';
|
||||
import { useAuthStore } from '../auth';
|
||||
// import { useAuthStore } from '../auth';
|
||||
import { useTabStore } from '../tab';
|
||||
import {
|
||||
filterAuthRoutesByRoles,
|
||||
@ -25,7 +25,7 @@ import {
|
||||
|
||||
export const useRouteStore = defineStore(SetupStoreId.Route, () => {
|
||||
const appStore = useAppStore();
|
||||
const authStore = useAuthStore();
|
||||
// const authStore = useAuthStore();
|
||||
const tabStore = useTabStore();
|
||||
const { bool: isInitAuthRoute, setBool: setIsInitAuthRoute } = useBoolean();
|
||||
const removeRouteFns: (() => void)[] = [];
|
||||
@ -160,7 +160,7 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
|
||||
async function initStaticAuthRoute() {
|
||||
const { authRoutes } = createRoutes();
|
||||
|
||||
const filteredAuthRoutes = filterAuthRoutesByRoles(authRoutes, authStore.userInfo.roles);
|
||||
const filteredAuthRoutes = filterAuthRoutesByRoles(authRoutes, []);
|
||||
|
||||
handleAuthRoutes(filteredAuthRoutes);
|
||||
|
||||
|
20
src/typings/api.d.ts
vendored
20
src/typings/api.d.ts
vendored
@ -52,14 +52,28 @@ declare namespace Api {
|
||||
*/
|
||||
namespace Auth {
|
||||
interface LoginToken {
|
||||
id: string;
|
||||
mode: string;
|
||||
role: String;
|
||||
token: string;
|
||||
refreshToken: string;
|
||||
createDt: string;
|
||||
updateDt: string;
|
||||
namespaceIds: NamespaceId[];
|
||||
}
|
||||
|
||||
interface UserInfo {
|
||||
userId: string;
|
||||
userName: string;
|
||||
roles: string[];
|
||||
id: string;
|
||||
mode: string;
|
||||
username: string;
|
||||
role: string;
|
||||
namespaceIds: NamespaceId[];
|
||||
}
|
||||
|
||||
interface NamespaceId {
|
||||
id: string;
|
||||
name: string;
|
||||
uniqueId: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
4
src/typings/app.d.ts
vendored
4
src/typings/app.d.ts
vendored
@ -631,9 +631,9 @@ declare namespace App {
|
||||
/** The backend service response data */
|
||||
type Response<T = unknown> = {
|
||||
/** The backend service response code */
|
||||
code: string;
|
||||
status: number;
|
||||
/** The backend service response message */
|
||||
msg: string;
|
||||
message: string;
|
||||
/** The backend service response data */
|
||||
data: T;
|
||||
};
|
||||
|
16
src/typings/components.d.ts
vendored
16
src/typings/components.d.ts
vendored
@ -16,21 +16,14 @@ declare module 'vue' {
|
||||
FullScreen: typeof import('./../components/common/full-screen.vue')['default']
|
||||
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
||||
IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
|
||||
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
|
||||
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||
IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
|
||||
IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
|
||||
IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
|
||||
IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
||||
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||
IconMdiDrag: typeof import('~icons/mdi/drag')['default']
|
||||
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
||||
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
||||
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||
@ -41,9 +34,6 @@ declare module 'vue' {
|
||||
NCard: typeof import('naive-ui')['NCard']
|
||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
NDescriptions: typeof import('naive-ui')['NDescriptions']
|
||||
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
|
||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||
NDivider: typeof import('naive-ui')['NDivider']
|
||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||
@ -52,7 +42,6 @@ declare module 'vue' {
|
||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||
NForm: typeof import('naive-ui')['NForm']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
||||
NGi: typeof import('naive-ui')['NGi']
|
||||
NGrid: typeof import('naive-ui')['NGrid']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
@ -65,10 +54,6 @@ declare module 'vue' {
|
||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
||||
NPopover: typeof import('naive-ui')['NPopover']
|
||||
NRadio: typeof import('naive-ui')['NRadio']
|
||||
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
@ -76,7 +61,6 @@ declare module 'vue' {
|
||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||
NTab: typeof import('naive-ui')['NTab']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
NThing: typeof import('naive-ui')['NThing']
|
||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
|
||||
|
2
src/typings/storage.d.ts
vendored
2
src/typings/storage.d.ts
vendored
@ -14,6 +14,8 @@ declare namespace StorageType {
|
||||
lang: App.I18n.LangType;
|
||||
/** The token */
|
||||
token: string;
|
||||
/** The namespace id */
|
||||
namespaceId: string;
|
||||
/** The refresh token */
|
||||
refreshToken: string;
|
||||
/** The user info */
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive } from 'vue';
|
||||
import { Md5 } from 'ts-md5';
|
||||
import { $t } from '@/locales';
|
||||
import { loginModuleRecord } from '@/constants/app';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
@ -20,8 +21,8 @@ interface FormModel {
|
||||
}
|
||||
|
||||
const model: FormModel = reactive({
|
||||
userName: 'Soybean',
|
||||
password: '123456'
|
||||
userName: 'admin',
|
||||
password: '654321'
|
||||
});
|
||||
|
||||
const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
|
||||
@ -35,7 +36,10 @@ const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
|
||||
|
||||
async function handleSubmit() {
|
||||
await validate();
|
||||
await authStore.login(model.userName, model.password);
|
||||
const md5 = new Md5();
|
||||
md5.appendAsciiStr(model.password);
|
||||
const password: string = md5.end() as string;
|
||||
await authStore.login(model.userName, password);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -48,7 +48,7 @@ const statisticData = computed<StatisticData[]>(() => [
|
||||
</div>
|
||||
<div class="pl-12px">
|
||||
<h3 class="text-18px font-semibold">
|
||||
{{ $t('page.home.greeting', { userName: authStore.userInfo.userName }) }}
|
||||
{{ $t('page.home.greeting', { userName: authStore.userInfo.username }) }}
|
||||
</h3>
|
||||
<p class="text-#999 leading-30px">{{ $t('page.home.weatherDesc') }}</p>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user