站内支持API接口地址切换 (#3162)

* feat: 站内切换接口API

* feat: 站内切换接口API

* feat: 站内支持API接口地址切换,解决冲突,新增开关显示

(cherry picked from commit c6d60b6cfd50bf8e19233a21640ecf1d41c01cc8)
This commit is contained in:
luchanan
2023-10-23 14:37:56 +08:00
committed by GitHub
parent b776ac4cd8
commit 00bf5d5bbf
11 changed files with 130 additions and 7 deletions

View File

@@ -11,6 +11,7 @@ export const ROLES_KEY = 'ROLES__KEY__';
// project config key // project config key
export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__'; export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__';
export const API_ADDRESS = 'API_ADDRESS__';
// lock info // lock info
export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__'; export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__';

View File

@@ -48,6 +48,8 @@ export function useHeaderSetting() {
const getShowDoc = computed(() => appStore.getHeaderSetting.showDoc); const getShowDoc = computed(() => appStore.getHeaderSetting.showDoc);
const getShowApi = computed(() => appStore.getHeaderSetting.showApi);
const getHeaderTheme = computed(() => appStore.getHeaderSetting.theme); const getHeaderTheme = computed(() => appStore.getHeaderSetting.theme);
const getShowHeader = computed(() => appStore.getHeaderSetting.show); const getShowHeader = computed(() => appStore.getHeaderSetting.show);
@@ -86,6 +88,7 @@ export function useHeaderSetting() {
setHeaderSetting, setHeaderSetting,
getShowDoc, getShowDoc,
getShowApi,
getShowSearch, getShowSearch,
getHeaderTheme, getHeaderTheme,
getUseLockPage, getUseLockPage,

View File

@@ -0,0 +1,82 @@
<template>
<BasicModal
:title="t('layout.header.dropdownChangeApi')"
v-bind="$attrs"
@register="register"
@ok="handelSubmit"
@cancel="handelCancel"
>
<BasicForm @register="registerForm">
<template #api="{ model, field }">
<RadioGroup v-model:value="model[field]">
<Radio :style="radioStyle" :value="key" v-for="(val, key) in addresses" :key="key"
>{{ key }}: {{ val }}</Radio
>
</RadioGroup>
</template>
</BasicForm>
</BasicModal>
</template>
<script lang="ts" setup>
import { Radio } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { BasicModal, useModalInner } from '/@/components/Modal/index';
import { BasicForm, useForm } from '/@/components/Form/index';
import { ref } from 'vue';
import { useAppStore } from '/@/store/modules/app';
import type { ApiAddress } from '/#/store';
const appStore = useAppStore();
const RadioGroup = Radio.Group;
const { t } = useI18n();
const [register, { closeModal }] = useModalInner(async () => {
initData();
});
// perf 能读取所有.env.xxx文件最好, 另外key与--mode XXX最好相同
const addresses = ref({
development: 'http://www.a.com',
test: 'http://www.b.com',
prod: 'http://www.c.com',
});
const radioStyle = ref({
display: 'flex',
height: '30px',
lineHeight: '30px',
});
const [registerForm, { validateFields, setFieldsValue }] = useForm({
showActionButtonGroup: false,
schemas: [
{
field: 'api',
label: t('layout.header.dropdownChangeApi'),
colProps: {
span: 24,
},
defaultValue: import.meta.env.MODE || 'development', // 当前环境
required: true,
component: 'Input',
slot: 'api',
},
],
});
const handelSubmit = async () => {
const values = await validateFields();
appStore.setApiAddress({
key: values.api,
val: addresses.value[values.api],
});
location.reload();
};
const handelCancel = () => {
closeModal();
};
const initData = () => {
const { key = '' } = appStore.getApiAddress as ApiAddress;
if (key) {
setFieldsValue({
api: key,
});
}
};
</script>
<style lang="less"></style>

View File

@@ -18,6 +18,12 @@
v-if="getShowDoc" v-if="getShowDoc"
/> />
<MenuDivider v-if="getShowDoc" /> <MenuDivider v-if="getShowDoc" />
<MenuItem
v-if="getShowApi"
key="api"
:text="t('layout.header.dropdownChangeApi')"
icon="ant-design:swap-outlined"
/>
<MenuItem <MenuItem
v-if="getUseLockPage" v-if="getUseLockPage"
key="lock" key="lock"
@@ -33,6 +39,7 @@
</template> </template>
</Dropdown> </Dropdown>
<LockAction @register="register" /> <LockAction @register="register" />
<ChangeApi @register="registerApi" />
</template> </template>
<script lang="ts"> <script lang="ts">
// components // components
@@ -55,7 +62,7 @@
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
type MenuEvent = 'logout' | 'doc' | 'lock'; type MenuEvent = 'logout' | 'doc' | 'lock' | 'api';
export default defineComponent({ export default defineComponent({
name: 'UserDropdown', name: 'UserDropdown',
@@ -65,6 +72,7 @@
MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')), MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')),
MenuDivider: Menu.Divider, MenuDivider: Menu.Divider,
LockAction: createAsyncComponent(() => import('../lock/LockModal.vue')), LockAction: createAsyncComponent(() => import('../lock/LockModal.vue')),
ChangeApi: createAsyncComponent(() => import('../ChangeApi/index.vue')),
}, },
props: { props: {
theme: propTypes.oneOf(['dark', 'light']), theme: propTypes.oneOf(['dark', 'light']),
@@ -72,7 +80,7 @@
setup() { setup() {
const { prefixCls } = useDesign('header-user-dropdown'); const { prefixCls } = useDesign('header-user-dropdown');
const { t } = useI18n(); const { t } = useI18n();
const { getShowDoc, getUseLockPage } = useHeaderSetting(); const { getShowDoc, getUseLockPage, getShowApi } = useHeaderSetting();
const userStore = useUserStore(); const userStore = useUserStore();
const getUserInfo = computed(() => { const getUserInfo = computed(() => {
@@ -81,11 +89,16 @@
}); });
const [register, { openModal }] = useModal(); const [register, { openModal }] = useModal();
const [registerApi, { openModal: openApiModal }] = useModal();
function handleLock() { function handleLock() {
openModal(true); openModal(true);
} }
function handleApi() {
openApiModal(true, {});
}
// login out // login out
function handleLoginOut() { function handleLoginOut() {
userStore.confirmLoginOut(); userStore.confirmLoginOut();
@@ -107,6 +120,9 @@
case 'lock': case 'lock':
handleLock(); handleLock();
break; break;
case 'api':
handleApi();
break;
} }
} }
@@ -116,7 +132,9 @@
getUserInfo, getUserInfo,
handleMenuClick, handleMenuClick,
getShowDoc, getShowDoc,
getShowApi,
register, register,
registerApi,
getUseLockPage, getUseLockPage,
}; };
}, },

View File

@@ -4,6 +4,7 @@
"onlineDocument": "Document" "onlineDocument": "Document"
}, },
"header": { "header": {
"dropdownChangeApi": "Change Api",
"dropdownItemDoc": "Document", "dropdownItemDoc": "Document",
"dropdownItemLoginOut": "Log Out", "dropdownItemLoginOut": "Log Out",
"tooltipErrorLog": "Error log", "tooltipErrorLog": "Error log",

View File

@@ -4,6 +4,7 @@
"onlineDocument": "在线文档" "onlineDocument": "在线文档"
}, },
"header": { "header": {
"dropdownChangeApi": "切换API",
"dropdownItemDoc": "文档", "dropdownItemDoc": "文档",
"dropdownItemLoginOut": "退出系统", "dropdownItemLoginOut": "退出系统",
"tooltipErrorLog": "错误日志", "tooltipErrorLog": "错误日志",

View File

@@ -74,6 +74,7 @@ const setting: ProjectConfig = {
showNotice: true, showNotice: true,
// Whether to display the menu search // Whether to display the menu search
showSearch: true, showSearch: true,
showApi: true,
}, },
// Menu configuration // Menu configuration

View File

@@ -5,13 +5,13 @@ import type {
TransitionSetting, TransitionSetting,
MultiTabsSetting, MultiTabsSetting,
} from '/#/config'; } from '/#/config';
import type { BeforeMiniState } from '/#/store'; import type { BeforeMiniState, ApiAddress } from '/#/store';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { store } from '/@/store'; import { store } from '/@/store';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
import { APP_DARK_MODE_KEY, PROJ_CFG_KEY } from '/@/enums/cacheEnum'; import { APP_DARK_MODE_KEY, PROJ_CFG_KEY, API_ADDRESS } from '/@/enums/cacheEnum';
import { Persistent } from '/@/utils/cache/persistent'; import { Persistent } from '/@/utils/cache/persistent';
import { darkMode } from '/@/settings/designSetting'; import { darkMode } from '/@/settings/designSetting';
import { resetRouter } from '/@/router'; import { resetRouter } from '/@/router';
@@ -63,6 +63,9 @@ export const useAppStore = defineStore({
getMultiTabsSetting(): MultiTabsSetting { getMultiTabsSetting(): MultiTabsSetting {
return this.getProjectConfig.multiTabsSetting; return this.getProjectConfig.multiTabsSetting;
}, },
getApiAddress() {
return JSON.parse(localStorage.getItem(API_ADDRESS) || '{}');
},
}, },
actions: { actions: {
setPageLoading(loading: boolean): void { setPageLoading(loading: boolean): void {
@@ -103,6 +106,9 @@ export const useAppStore = defineStore({
clearTimeout(timeId); clearTimeout(timeId);
} }
}, },
setApiAddress(config: ApiAddress): void {
localStorage.setItem(API_ADDRESS, JSON.stringify(config));
},
}, },
}); });

View File

@@ -1,5 +1,6 @@
import type { GlobEnvConfig } from '/#/config'; import type { GlobEnvConfig } from '/#/config';
import pkg from '../../package.json'; import pkg from '../../package.json';
import { API_ADDRESS } from '/@/enums/cacheEnum';
export function getCommonStoragePrefix() { export function getCommonStoragePrefix() {
const { VITE_GLOB_APP_TITLE } = getAppEnvConfig(); const { VITE_GLOB_APP_TITLE } = getAppEnvConfig();
@@ -29,9 +30,12 @@ export function getAppEnvConfig() {
? // Get the global configuration (the configuration will be extracted independently when packaging) ? // Get the global configuration (the configuration will be extracted independently when packaging)
(import.meta.env as unknown as GlobEnvConfig) (import.meta.env as unknown as GlobEnvConfig)
: (window[ENV_NAME] as unknown as GlobEnvConfig); : (window[ENV_NAME] as unknown as GlobEnvConfig);
const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL } = const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL } = ENV;
ENV; let { VITE_GLOB_API_URL } = ENV;
if (localStorage.getItem(API_ADDRESS)) {
const address = JSON.parse(localStorage.getItem(API_ADDRESS) || '{}');
if (address?.key) VITE_GLOB_API_URL = address?.val;
}
return { return {
VITE_GLOB_APP_TITLE, VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL, VITE_GLOB_API_URL,

1
types/config.d.ts vendored
View File

@@ -57,6 +57,7 @@ export interface HeaderSetting {
// Show message center button // Show message center button
showNotice: boolean; showNotice: boolean;
showSearch: boolean; showSearch: boolean;
showApi: boolean;
} }
export interface LocaleSetting { export interface LocaleSetting {

5
types/store.d.ts vendored
View File

@@ -10,6 +10,11 @@ export interface LockInfo {
isLock?: boolean; isLock?: boolean;
} }
export interface ApiAddress {
key: string;
val: string;
}
// Error-log information // Error-log information
export interface ErrorLogInfo { export interface ErrorLogInfo {
// Type of error // Type of error