feat(passport): 优化登录页账号/手机号切换和指示器动画

- 调整默认登录类型为账号登录,支持账号和手机号登录切换
- 添加底部蓝色下划线指示器,动态显示当前选中标签位置和宽度
- 实现指示器收缩滑动和展开的两阶段动画效果,提升交互体验
- 增加窗口尺寸变化时自动调整指示器位置和宽度
- 修改扫码登录切换逻辑,避免意外切换至手机号登录
- 在账号登录和手机号登录页底部添加提示“还没有账号?联系管理员开通”
This commit is contained in:
2026-07-18 17:10:08 +08:00
parent 2037b0699a
commit 9395e63f20

View File

@@ -32,18 +32,21 @@
</template>
<template v-else>
<h4
class="title-btn"
:class="loginType === 'sms' ? 'active' : ''"
@click="onLoginType('sms')"
>手机号登录</h4
>
<a-divider type="vertical" style="height: 20px" />
<h4
ref="accountTabRef"
class="title-btn"
:class="loginType === 'account' ? 'active' : ''"
@click="onLoginType('account')"
>账号登录</h4
>
<a-divider type="vertical" style="height: 20px" />
<h4
ref="smsTabRef"
class="title-btn"
:class="loginType === 'sms' ? 'active' : ''"
@click="onLoginType('sms')"
>手机号登录</h4
>
<div class="tab-indicator" :style="indicatorStyle"></div>
</template>
</div>
<div
@@ -59,7 +62,7 @@
<div
class="absolute top-2 right-2 text-lg text-white font-bold cursor-pointer"
>
<QrcodeOutlined v-if="loginType === 'sms'" />
<QrcodeOutlined v-if="loginType === 'scan'" />
<MobileOutlined v-else />
</div>
<!-- <span class="absolute top-3 right-1.5 text-sm text-white font-bold cursor-pointer">{{ '登录' }}</span>-->
@@ -141,6 +144,9 @@
>
{{ loading ? t('login.loading') : t('login.login') }}
</a-button>
<div class="text-center pt-5 text-gray-500"
>还没有账号联系管理员开通</div
>
</a-form-item>
</template>
<template v-if="loginType === 'sms'">
@@ -185,6 +191,9 @@
>
{{ loading ? t('login.loading') : t('login.login') }}
</a-button>
<div class="text-center pt-5 text-gray-500"
>还没有账号联系管理员开通</div
>
</a-form-item>
</template>
<template v-if="loginType == 'scan'">
@@ -265,7 +274,7 @@
</template>
<script lang="ts" setup>
import { ref, reactive, unref, watch } from 'vue';
import { ref, reactive, unref, watch, nextTick, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { Form, message } from 'ant-design-vue';
@@ -313,8 +322,62 @@
const direction = ref(0);
// 加载状态
const loading = ref(false);
// 是否显示tenantId填写输入框
const loginType = ref('sms');
// 是否显示tenantId填写输入框,默认进入账号登录/手机号登录切换模式
const loginType = ref('account');
// 标签下划线指示器
const accountTabRef = ref<HTMLElement>();
const smsTabRef = ref<HTMLElement>();
const indicatorStyle = ref({ left: '0px', width: '0px', transition: 'none' });
// 收缩/展开两阶段动画时长(ms),模拟阿里云"先收一下再展开"的弹性效果
const SHRINK_MS = 150;
const EXPAND_MS = 220;
let indicatorTimer: number | null = null;
const updateIndicator = (animate = true) => {
if (indicatorTimer != null) {
clearTimeout(indicatorTimer);
indicatorTimer = null;
}
nextTick(() => {
const activeEl =
loginType.value === 'account' ? accountTabRef.value : smsTabRef.value;
if (!activeEl) {
return;
}
const targetLeft = activeEl.offsetLeft;
const targetWidth = activeEl.offsetWidth;
// 非动画:直接定位(首次渲染 / 窗口缩放)
if (!animate) {
indicatorStyle.value = {
left: `${targetLeft}px`,
width: `${targetWidth}px`,
transition: 'none'
};
return;
}
// 阶段一:收缩成一条短线,并滑向目标中心
const targetCenter = targetLeft + targetWidth / 2;
const smallWidth = 24;
indicatorStyle.value = {
left: `${targetCenter - smallWidth / 2}px`,
width: `${smallWidth}px`,
transition: `left ${SHRINK_MS}ms ease-in, width ${SHRINK_MS}ms ease-in`
};
// 阶段二:展开到目标标签的宽度
indicatorTimer = window.setTimeout(() => {
indicatorStyle.value = {
left: `${targetLeft}px`,
width: `${targetWidth}px`,
transition: `left ${EXPAND_MS}ms ease-out, width ${EXPAND_MS}ms ease-out`
};
indicatorTimer = null;
}, SHRINK_MS);
});
};
watch(loginType, () => updateIndicator(true), { immediate: true });
onMounted(() => {
updateIndicator(false);
window.addEventListener('resize', () => updateIndicator(false));
});
// 默认配置
const defaultConfig: Config = {
@@ -588,7 +651,7 @@
const onScan = () => {
if (loginType.value == 'scan') {
loginType.value = 'sms';
loginType.value = 'account';
} else {
loginType.value = 'scan';
}
@@ -713,9 +776,11 @@
.title-btn {
cursor: pointer;
white-space: nowrap;
}
.login-title {
position: relative;
display: flex;
justify-content: space-around;
line-height: 50px;
@@ -723,6 +788,14 @@
.active {
color: #007dff;
}
.tab-indicator {
position: absolute;
bottom: -10px;
height: 3px;
border-radius: 2px;
background-color: #007dff;
}
}
}