瀏覽代碼

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

* feat: 站内切换接口API

* feat: 站内切换接口API

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

(cherry picked from commit c6d60b6cfd50bf8e19233a21640ecf1d41c01cc8)
luchanan 1 年之前
父節點
當前提交
00bf5d5bbf

+ 1 - 0
src/enums/cacheEnum.ts

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

+ 3 - 0
src/hooks/setting/useHeaderSetting.ts

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

+ 82 - 0
src/layouts/default/header/components/ChangeApi/index.vue

@@ -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>

+ 20 - 2
src/layouts/default/header/components/user-dropdown/index.vue

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

+ 1 - 0
src/locales/lang/en/layout.json

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

+ 1 - 0
src/locales/lang/zh-CN/layout.json

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

+ 1 - 0
src/settings/projectSetting.ts

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

+ 8 - 2
src/store/modules/app.ts

@@ -5,13 +5,13 @@ import type {
   TransitionSetting,
   MultiTabsSetting,
 } from '/#/config';
-import type { BeforeMiniState } from '/#/store';
+import type { BeforeMiniState, ApiAddress } from '/#/store';
 
 import { defineStore } from 'pinia';
 import { store } from '/@/store';
 
 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 { darkMode } from '/@/settings/designSetting';
 import { resetRouter } from '/@/router';
@@ -63,6 +63,9 @@ export const useAppStore = defineStore({
     getMultiTabsSetting(): MultiTabsSetting {
       return this.getProjectConfig.multiTabsSetting;
     },
+    getApiAddress() {
+      return JSON.parse(localStorage.getItem(API_ADDRESS) || '{}');
+    },
   },
   actions: {
     setPageLoading(loading: boolean): void {
@@ -103,6 +106,9 @@ export const useAppStore = defineStore({
         clearTimeout(timeId);
       }
     },
+    setApiAddress(config: ApiAddress): void {
+      localStorage.setItem(API_ADDRESS, JSON.stringify(config));
+    },
   },
 });
 

+ 7 - 3
src/utils/env.ts

@@ -1,5 +1,6 @@
 import type { GlobEnvConfig } from '/#/config';
 import pkg from '../../package.json';
+import { API_ADDRESS } from '/@/enums/cacheEnum';
 
 export function getCommonStoragePrefix() {
   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)
       (import.meta.env 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 } =
-    ENV;
-
+  const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL } = 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 {
     VITE_GLOB_APP_TITLE,
     VITE_GLOB_API_URL,

+ 1 - 0
types/config.d.ts

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

+ 5 - 0
types/store.d.ts

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