refactor: 使用antd组件替换密码登录表单
- 废弃原有的shadcn-ui验证码组件,改用antd实现 - 更新登录表单中的验证码输入组件为新的antd实现 - 调整表单布局和样式
This commit is contained in:
parent
dbe8beb7f9
commit
1383f63361
@ -2,6 +2,10 @@
|
||||
|
||||
dev中 未发布
|
||||
|
||||
**REFACTOR**
|
||||
|
||||
- 使用antd组件替换密码登录表单
|
||||
|
||||
**FEATURES**
|
||||
|
||||
- 合并官方更新 将Radix(v1)替换为Reka UI(v2)
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
import { Input } from 'ant-design-vue';
|
||||
|
||||
interface Props {
|
||||
captcha?: string;
|
||||
label?: string;
|
||||
loading?: boolean;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
captcha: '',
|
||||
label: '验证码',
|
||||
loading: false,
|
||||
placeholder: '请输入验证码',
|
||||
});
|
||||
|
||||
defineEmits<{ captchaClick: [] }>();
|
||||
|
||||
const modelValue = defineModel<string>({ default: '' });
|
||||
</script>
|
||||
|
||||
<!-- 图片验证码 -->
|
||||
<template>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<Input
|
||||
size="large"
|
||||
id="code"
|
||||
name="code"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
required
|
||||
v-model:value="modelValue"
|
||||
:class="$attrs?.class ?? {}"
|
||||
:label="label"
|
||||
:placeholder="placeholder"
|
||||
/>
|
||||
</div>
|
||||
<div class="captcha-image--container relative">
|
||||
<img
|
||||
:src="captcha"
|
||||
class="h-[40px] w-[115px] cursor-pointer rounded-r-lg"
|
||||
:class="{ 'pointer-events-none': loading }"
|
||||
@click="$emit('captchaClick')"
|
||||
/>
|
||||
<div
|
||||
v-if="loading"
|
||||
class="absolute inset-0 flex cursor-not-allowed items-center justify-center rounded-r-lg bg-black/30"
|
||||
>
|
||||
<span class="captcha-loading"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@keyframes loading-rotation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.captcha-loading {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 2px solid #fff;
|
||||
border-bottom-color: transparent;
|
||||
border-radius: 50%;
|
||||
animation: loading-rotation 1s linear infinite;
|
||||
}
|
||||
|
||||
/**
|
||||
验证码输入框样式
|
||||
去除右边的圆角
|
||||
*/
|
||||
input[id='code'] {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
</style>
|
||||
@ -4,12 +4,13 @@ import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
|
||||
import type { TenantResp } from '#/api';
|
||||
import type { CaptchaResponse } from '#/api/core/captcha';
|
||||
|
||||
import { computed, onMounted, ref, useTemplateRef } from 'vue';
|
||||
import { computed, markRaw, onMounted, ref, useTemplateRef } from 'vue';
|
||||
|
||||
import { AuthenticationLogin, z } from '@vben/common-ui';
|
||||
import { DEFAULT_TENANT_ID } from '@vben/constants';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { Input, Select } from 'ant-design-vue';
|
||||
import { omit } from 'lodash-es';
|
||||
|
||||
import { tenantList } from '#/api';
|
||||
@ -17,6 +18,7 @@ import { captchaImage } from '#/api/core/captcha';
|
||||
import { useAuthStore } from '#/store';
|
||||
|
||||
import { useLoginTenantId } from '../oauth-common';
|
||||
import InputCaptcha from './input-captcha.vue';
|
||||
import OAuthLogin from './oauth-login.vue';
|
||||
|
||||
defineOptions({ name: 'Login' });
|
||||
@ -73,15 +75,18 @@ const { loginTenantId } = useLoginTenantId();
|
||||
const formSchema = computed((): VbenFormSchema[] => {
|
||||
return [
|
||||
{
|
||||
component: 'VbenSelect',
|
||||
component: markRaw(Select),
|
||||
modelPropName: 'value',
|
||||
componentProps: {
|
||||
class: 'bg-background h-[40px] focus:border-primary',
|
||||
contentClass: 'max-h-[256px] overflow-y-auto',
|
||||
class: 'w-full',
|
||||
size: 'large',
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
options: tenantInfo.value.voList?.map((item) => ({
|
||||
label: item.companyName,
|
||||
value: item.tenantId,
|
||||
})),
|
||||
placeholder: $t('authentication.selectAccount'),
|
||||
placeholder: $t('ui.formRules.selectRequired'),
|
||||
},
|
||||
defaultValue: DEFAULT_TENANT_ID,
|
||||
dependencies: {
|
||||
@ -98,10 +103,12 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules: z.string().min(1, { message: $t('authentication.selectAccount') }),
|
||||
},
|
||||
{
|
||||
component: 'VbenInput',
|
||||
component: markRaw(Input),
|
||||
modelPropName: 'value',
|
||||
componentProps: {
|
||||
class: 'focus:border-primary',
|
||||
size: 'large',
|
||||
placeholder: $t('authentication.usernameTip'),
|
||||
allowClear: true,
|
||||
},
|
||||
defaultValue: 'admin',
|
||||
fieldName: 'username',
|
||||
@ -109,10 +116,11 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules: z.string().min(1, { message: $t('authentication.usernameTip') }),
|
||||
},
|
||||
{
|
||||
component: 'VbenInputPassword',
|
||||
component: markRaw(Input.Password),
|
||||
modelPropName: 'value',
|
||||
componentProps: {
|
||||
class: 'focus:border-primary',
|
||||
placeholder: $t('authentication.password'),
|
||||
size: 'large',
|
||||
placeholder: $t('authentication.passwordTip'),
|
||||
},
|
||||
defaultValue: 'admin123',
|
||||
fieldName: 'password',
|
||||
@ -120,7 +128,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules: z.string().min(5, { message: $t('authentication.passwordTip') }),
|
||||
},
|
||||
{
|
||||
component: 'VbenInputCaptcha',
|
||||
component: markRaw(InputCaptcha),
|
||||
componentProps: {
|
||||
captcha: captchaInfo.value.img,
|
||||
class: 'focus:border-primary',
|
||||
|
||||
@ -1 +1,4 @@
|
||||
/**
|
||||
* @deprecated 使用antd组件实现来代替
|
||||
*/
|
||||
export { default as VbenInputCaptcha } from './input-captcha.vue';
|
||||
|
||||
@ -109,7 +109,7 @@ defineExpose({
|
||||
</Title>
|
||||
</slot>
|
||||
|
||||
<Form />
|
||||
<Form class="mb-2" />
|
||||
|
||||
<div
|
||||
v-if="showRememberMe || showForgetPassword"
|
||||
|
||||
@ -20,7 +20,7 @@ defineProps<{
|
||||
<component
|
||||
:is="Component"
|
||||
:key="route.fullPath"
|
||||
class="side-content mt-6 w-full sm:mx-auto md:max-w-md"
|
||||
class="side-content mt-6 w-full sm:mx-auto md:max-w-md lg:max-w-[400px]"
|
||||
:data-side="dataSide"
|
||||
/>
|
||||
</KeepAlive>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user