diff --git a/apps/web-antd/src/api/core/auth.ts b/apps/web-antd/src/api/core/auth.ts index 4071577dd..576446b45 100644 --- a/apps/web-antd/src/api/core/auth.ts +++ b/apps/web-antd/src/api/core/auth.ts @@ -24,13 +24,13 @@ export namespace AuthApi { } /** 手机验证码获取接口参数 */ - export interface SmsCodeVO { + export interface SmsCodeParams { mobile: string; scene: number; } /** 手机验证码登录接口参数 */ - export interface SmsLoginVO { + export interface SmsLoginParams { mobile: string; code: string; } @@ -83,4 +83,14 @@ export async function getCaptcha(data: any) { /** 校验验证码 */ export async function checkCaptcha(data: any) { return baseRequestClient.post('/system/captcha/check', data); +} + +/** 获取登录验证码 */ +export const sendSmsCode = (data: AuthApi.SmsCodeParams) => { + return requestClient.post('/system/auth/send-sms-code', data ) +} + +/** 短信验证码登录 */ +export const smsLogin = (data: AuthApi.SmsLoginParams) => { + return requestClient.post('/system/auth/sms-login', data) } \ No newline at end of file diff --git a/apps/web-antd/src/store/auth.ts b/apps/web-antd/src/store/auth.ts index e4abf9c0a..543b77ed1 100644 --- a/apps/web-antd/src/store/auth.ts +++ b/apps/web-antd/src/store/auth.ts @@ -9,7 +9,7 @@ import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores'; import { notification } from 'ant-design-vue'; import { defineStore } from 'pinia'; -import { getAuthPermissionInfoApi, loginApi, logoutApi} from '#/api'; +import { type AuthApi, getAuthPermissionInfoApi, loginApi, logoutApi, smsLogin } from '#/api'; import { $t } from '#/locales'; export const useAuthStore = defineStore('auth', () => { @@ -22,9 +22,12 @@ export const useAuthStore = defineStore('auth', () => { /** * 异步处理登录操作 * Asynchronously handle the login process + * @param type 登录类型 * @param params 登录表单数据 + * @param onSuccess 登录成功后的回调函数 */ async function authLogin( + type: 'mobile' | 'username', params: Recordable, onSuccess?: () => Promise | void, ) { @@ -32,7 +35,8 @@ export const useAuthStore = defineStore('auth', () => { let userInfo: null | UserInfo = null; try { loginLoading.value = true; - const { accessToken, refreshToken } = await loginApi(params); + const { accessToken, refreshToken } = type === 'mobile' ? await smsLogin(params as AuthApi.SmsLoginParams) + : await loginApi(params); // 如果成功获取到 accessToken if (accessToken) { diff --git a/apps/web-antd/src/views/_core/authentication/code-login.vue b/apps/web-antd/src/views/_core/authentication/code-login.vue index acfd1fd78..d3225ee1c 100644 --- a/apps/web-antd/src/views/_core/authentication/code-login.vue +++ b/apps/web-antd/src/views/_core/authentication/code-login.vue @@ -2,24 +2,102 @@ import type { VbenFormSchema } from '@vben/common-ui'; import type { Recordable } from '@vben/types'; -import { computed, ref } from 'vue'; +import { computed, ref, onMounted } from 'vue'; import { AuthenticationCodeLogin, z } from '@vben/common-ui'; import { $t } from '@vben/locales'; +import { type AuthApi, sendSmsCode } from '#/api'; +import { useAppConfig } from '@vben/hooks'; +import { message } from 'ant-design-vue'; + +import { getTenantSimpleList, getTenantByWebsite } from '#/api/core/auth'; +import { useAccessStore } from '@vben/stores'; +import { useAuthStore } from '#/store'; +const { tenantEnable } = useAppConfig(import.meta.env, import.meta.env.PROD); defineOptions({ name: 'CodeLogin' }); +const authStore = useAuthStore(); +const accessStore = useAccessStore(); + const loading = ref(false); -const CODE_LENGTH = 6; +const CODE_LENGTH = 4; + +const loginRef = ref(); + +/** 获取租户列表,并默认选中 */ +const tenantList = ref([]); // 租户列表 +const fetchTenantList = async () => { + if (!tenantEnable) { + return; + } + try { + // 获取租户列表、域名对应租户 + const websiteTenantPromise = getTenantByWebsite(window.location.hostname); + tenantList.value = await getTenantSimpleList(); + + // 选中租户:域名 > store 中的租户 > 首个租户 + let tenantId: number | null = null; + const websiteTenant = await websiteTenantPromise; + if (websiteTenant?.id) { + tenantId = websiteTenant.id; + } + // 如果没有从域名获取到租户,尝试从 store 中获取 + if (!tenantId && accessStore.tenantId) { + tenantId = accessStore.tenantId; + } + // 如果还是没有租户,使用列表中的第一个 + if (!tenantId && tenantList.value?.[0]?.id) { + tenantId = tenantList.value[0].id; + } + + // 设置选中的租户编号 + accessStore.setTenantId(tenantId); + loginRef.value.getFormApi().setFieldValue('tenantId', tenantId); + } catch (error) { + console.error('获取租户列表失败:', error); + } +}; + +/** 组件挂载时获取租户信息 */ +onMounted(() => { + fetchTenantList(); +}); const formSchema = computed((): VbenFormSchema[] => { return [ + { + component: 'VbenSelect', + componentProps: { + options: tenantList.value.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: $t('authentication.tenantTip'), + }, + fieldName: 'tenantId', + label: $t('authentication.tenant'), + rules: z + .number() + .nullable() + .refine((val) => val != null && val > 0, $t('authentication.tenantTip')) + .default(null), + dependencies: { + triggerFields: ['tenantId'], + if: tenantEnable, + trigger(values) { + if (values.tenantId) { + accessStore.setTenantId(values.tenantId); + } + }, + }, + }, { component: 'VbenInput', componentProps: { placeholder: $t('authentication.mobile'), }, - fieldName: 'phoneNumber', + fieldName: 'mobile', label: $t('authentication.mobile'), rules: z .string() @@ -40,6 +118,29 @@ const formSchema = computed((): VbenFormSchema[] => { return text; }, placeholder: $t('authentication.code'), + handleSendCode: async () => { + loading.value = true; + try { + const formApi = loginRef.value?.getFormApi(); + if (!formApi) { + throw new Error('表单未准备好'); + } + // 验证手机号 + await formApi.validateField('mobile'); + const isMobileValid = await formApi.isFieldValid('mobile'); + if (!isMobileValid) { + throw new Error('请输入有效的手机号码'); + } + + // 发送验证码 + const { mobile } = await formApi.getValues(); + const scene = 21; // 场景:短信验证码登录 + await sendSmsCode({ mobile, scene }); + message.success('验证码发送成功'); + } finally { + loading.value = false; + } + } }, fieldName: 'code', label: $t('authentication.code'), @@ -55,13 +156,17 @@ const formSchema = computed((): VbenFormSchema[] => { * @param values 登录表单数据 */ async function handleLogin(values: Recordable) { - // eslint-disable-next-line no-console - console.log(values); + try { + await authStore.authLogin('mobile', values); + } catch (error) { + console.error('Error in handleLogin:', error); + } }