Procházet zdrojové kódy

feat(tenant): 添加租户管理相关功能和路由

- 新增接口fetchGetTenantOptions获取租户选项列表
- 登录接口fetchLogin支持tenantCode参数及请求头X-Tenant-Domain
- 登录组件增加租户选择下拉,提交时携带租户编码
- 路由配置中新增tenant模块及其子路由tenant_tenant和tenant_tenant-package
- 本地化文件中添加租户管理相关的中英文字段
- 路由导入、路径映射和类型声明添加租户相关配置
- 测试环境配置文件新增本地租户服务地址
zouzexu před 2 dny
rodič
revize
8711ed9f23

+ 2 - 1
.env.test

@@ -7,9 +7,10 @@
 # VITE_SERVICE_BASE_URL=http://74949mkfh190.vicp.fun #付
 # VITE_SERVICE_BASE_URL=https://smqjh.api.zswlgz.com
 # VITE_SERVICE_BASE_URL=https://735a1bda.r24.cpolar.top #黄
+VITE_SERVICE_BASE_URL=http://192.168.0.11:8080 #wzq
 # VITE_SERVICE_BASE_URL=http://89561bkaq794.vicp.fun:53846
 
-VITE_SERVICE_BASE_URL=http://47.109.84.152:8081#打包测试本地服务器
+#VITE_SERVICE_BASE_URL=http://47.109.84.152:8081#打包测试本地服务器
 # VITE_SERVICE_BASE_URL=https://smqjh.api.zswlgz.com #服务器
 
 # other backend service base url, test environment

+ 4 - 1
src/locales/langs/en-us.ts

@@ -298,7 +298,10 @@ const local: App.I18n.Schema = {
     'member-center_edit-member-type': '',
     'member-center_member-list': '',
     'member-center_member-order': '',
-    'member-center_member-type': ''
+    'member-center_member-type': '',
+    tenant: 'Tenant Management',
+    tenant_tenant: 'Tenant Management',
+    'tenant_tenant-package': 'Tenant Package'
   },
   page: {
     login: {

+ 4 - 1
src/locales/langs/zh-cn.ts

@@ -295,7 +295,10 @@ const local: App.I18n.Schema = {
     'member-center_edit-member-type': '',
     'member-center_member-list': '',
     'member-center_member-order': '',
-    'member-center_member-type': ''
+    'member-center_member-type': '',
+    tenant: '租户管理',
+    tenant_tenant: '租户管理',
+    'tenant_tenant-package': '租户套餐'
   },
   page: {
     login: {

+ 2 - 0
src/router/elegant/imports.ts

@@ -62,6 +62,8 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
   "order-manage_order-detail": () => import("@/views/order-manage/order-detail/index.vue"),
   "order-manage_ponits-details": () => import("@/views/order-manage/ponits-details/index.vue"),
   "order-manage_recharge-records": () => import("@/views/order-manage/recharge-records/index.vue"),
+  "tenant_tenant-package": () => import("@/views/tenant/tenant-package/index.vue"),
+  tenant_tenant: () => import("@/views/tenant/tenant/index.vue"),
   "user-center": () => import("@/views/user-center/index.vue"),
   "user-management_user-list": () => import("@/views/user-management/user-list/index.vue"),
   "xsb-manage_advertisement": () => import("@/views/xsb-manage/advertisement/index.vue"),

+ 30 - 0
src/router/elegant/routes.ts

@@ -565,6 +565,36 @@ export const generatedRoutes: GeneratedRoute[] = [
       }
     ]
   },
+  {
+    name: 'tenant',
+    path: '/tenant',
+    component: 'layout.base',
+    meta: {
+      title: 'tenant',
+      i18nKey: 'route.tenant',
+      order: 90
+    },
+    children: [
+      {
+        name: 'tenant_tenant',
+        path: '/tenant/tenant',
+        component: 'view.tenant_tenant',
+        meta: {
+          title: 'tenant_tenant',
+          i18nKey: 'route.tenant_tenant'
+        }
+      },
+      {
+        name: 'tenant_tenant-package',
+        path: '/tenant/tenant-package',
+        component: 'view.tenant_tenant-package',
+        meta: {
+          title: 'tenant_tenant-package',
+          i18nKey: 'route.tenant_tenant-package'
+        }
+      }
+    ]
+  },
   {
     name: 'user-center',
     path: '/user-center',

+ 3 - 0
src/router/elegant/transform.ts

@@ -235,6 +235,9 @@ const routeMap: RouteMap = {
   "order-manage_order-detail": "/order-manage/order-detail",
   "order-manage_ponits-details": "/order-manage/ponits-details",
   "order-manage_recharge-records": "/order-manage/recharge-records",
+  "tenant": "/tenant",
+  "tenant_tenant": "/tenant/tenant",
+  "tenant_tenant-package": "/tenant/tenant-package",
   "user-center": "/user-center",
   "user-management": "/user-management",
   "user-management_user-list": "/user-management/user-list",

+ 20 - 2
src/service/api/auth.ts

@@ -1,5 +1,13 @@
 import { request } from '../request';
 
+/** 获取租户选项列表 */
+export function fetchGetTenantOptions() {
+  return request<{ id: number; tenantCode: string; tenantName: string }[]>({
+    url: '/smqjh-system/api/v1/tenants/options',
+    method: 'get'
+  });
+}
+
 // /**
 //  * Login
 //  *
@@ -26,19 +34,29 @@ import { request } from '../request';
  * @param data
  * @returns
  */
-export function fetchLogin(data: { username: string; password: string; captchaCode: string; captchaKey: string }) {
+export function fetchLogin(data: {
+  username: string;
+  password: string;
+  captchaCode: string;
+  captchaKey: string;
+  tenantCode?: string;
+}) {
   const formData = new FormData();
   formData.append('username', data.username);
   formData.append('password', data.password);
   formData.append('captchaId', data.captchaKey as string);
   formData.append('captchaCode', data.captchaCode as string);
   formData.append('grant_type', 'password');
+  if (data.tenantCode) {
+    formData.append('tenantCode', data.tenantCode);
+  }
   return request<Api.Auth.LoginToken>({
     url: '/smqjh-auth/oauth2/token',
     method: 'post',
     data: formData,
     headers: {
-      'Content-Type': 'multipart/form-data'
+      'Content-Type': 'multipart/form-data',
+      'X-Tenant-Domain': '192.168.0.11'
     }
   });
 }

+ 4 - 3
src/store/modules/auth/index.ts

@@ -102,10 +102,10 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
    * @param [redirect=true] Whether to redirect after login. Default is `true`
    */
   async function login(
-    model: { userName: string; password: string; captchaCode: string; captchaKey: string },
+    model: { userName: string; password: string; captchaCode: string; captchaKey: string; tenantCode?: string },
     redirect = true
   ) {
-    const { userName, password, captchaCode, captchaKey } = model;
+    const { userName, password, captchaCode, captchaKey, tenantCode } = model;
     console.log(model, 'model');
 
     startLoading();
@@ -113,7 +113,8 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
       username: userName,
       password,
       captchaCode,
-      captchaKey
+      captchaKey,
+      tenantCode
     });
     console.log('login', data);
 

+ 6 - 0
src/typings/elegant-router.d.ts

@@ -89,6 +89,9 @@ declare module "@elegant-router/types" {
     "order-manage_order-detail": "/order-manage/order-detail";
     "order-manage_ponits-details": "/order-manage/ponits-details";
     "order-manage_recharge-records": "/order-manage/recharge-records";
+    "tenant": "/tenant";
+    "tenant_tenant": "/tenant/tenant";
+    "tenant_tenant-package": "/tenant/tenant-package";
     "user-center": "/user-center";
     "user-management": "/user-management";
     "user-management_user-list": "/user-management/user-list";
@@ -159,6 +162,7 @@ declare module "@elegant-router/types" {
     | "member-center"
     | "operation"
     | "order-manage"
+    | "tenant"
     | "user-center"
     | "user-management"
     | "xsb-manage"
@@ -227,6 +231,8 @@ declare module "@elegant-router/types" {
     | "order-manage_order-detail"
     | "order-manage_ponits-details"
     | "order-manage_recharge-records"
+    | "tenant_tenant-package"
+    | "tenant_tenant"
     | "user-center"
     | "user-management_user-list"
     | "xsb-manage_advertisement"

+ 25 - 1
src/views/_builtin/login/modules/pwd-login.vue

@@ -2,6 +2,7 @@
 import { computed, onMounted, reactive, ref, watch } from 'vue';
 import { Crypto } from '@sa/utils';
 import { fetchGetCaptcha } from '@/service/api/common';
+import { fetchGetTenantOptions } from '@/service/api/auth';
 import { useAuthStore } from '@/store/modules/auth';
 import { useFormRules, useNaiveForm } from '@/hooks/common/form';
 import { localStg } from '@/utils/storage';
@@ -15,6 +16,22 @@ const authStore = useAuthStore();
 const { formRef, validate } = useNaiveForm();
 const isRememberMe = ref(false);
 const imgPath = ref('');
+
+// 租户选项
+const tenantOptions = ref<{ label: string; value: string }[]>([]);
+const selectedTenantCode = ref<string | null>(null);
+
+async function loadTenantOptions() {
+  const { data } = await fetchGetTenantOptions();
+  if (data) {
+    tenantOptions.value = (data as any[]).map((item: any) => ({
+      label: item.tenantName,
+      value: item.tenantCode
+    }));
+  }
+}
+loadTenantOptions();
+
 interface FormModel {
   userName: string;
   password: string;
@@ -51,12 +68,16 @@ const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
 const captchaKey = ref('');
 async function handleSubmit() {
   await validate();
+  if (!selectedTenantCode.value) {
+    window.$message?.error('请选择租户');
+    return;
+  }
   if (isRememberMe.value) {
     const pwd = pd.encrypt({ data: model.password });
     localStg.set('userName', model.userName);
     localStg.set('password', pwd);
   }
-  await authStore.login({ ...model, captchaKey: captchaKey.value });
+  await authStore.login({ ...model, captchaKey: captchaKey.value, tenantCode: selectedTenantCode.value || undefined });
 }
 async function getDataCode() {
   const { data } = await fetchGetCaptcha();
@@ -77,6 +98,9 @@ watch(
 
 <template>
   <NForm ref="formRef" :model="model" :rules="rules" size="large" :show-label="false" @keyup.enter="handleSubmit">
+    <NFormItem :rule="{ required: true, message: '请选择租户', trigger: 'change' }">
+      <NSelect v-model:value="selectedTenantCode" :options="tenantOptions" placeholder="请选择租户" />
+    </NFormItem>
     <NFormItem path="userName">
       <NInput v-model:value="model.userName" :placeholder="$t('page.login.common.userNamePlaceholder')" />
     </NFormItem>