新增: 登录页 UI 重构 + 品牌 logo 与 Lottie 启动动画
视觉与品牌 - public/logo.svg、public/login-illustration.svg、favicon 替换为 logo.svg。 - preferences.ts 新增 logo.source 指向 /logo.svg。 - packages/effects/layouts 导出 LoginIllustration 共享组件(浮动插画,加载失败自动隐藏)。 Auth 布局重构 - layouts/auth.vue 替换默认 AuthPageLayout:主题色渐变背景 + 左侧浮动插画 + 品牌卡片 + 右上 LanguageToggle + 圆角登录卡片;KeepAlive 仅缓存 Login (CodeLogin/QrCodeLogin 需要每次刷新验证码/二维码)。 - 表单样式通过 :deep(.login-form-container) 覆盖输入框、选择框、主按钮, 全部走 --primary 变量,与主题色联动。 登录组件 - views/_core/authentication/login.vue: 关闭默认 codeLogin/qrcodeLogin/ thirdPartyLogin/register/docLink,改为手机 / 二维码 / 企微三按钮(IconifyIcon); 三方登录前检查租户,缺失时 message.warning + validateField。 - packages/effects/common-ui/authentication/login.vue + types.ts: 新增 showDocLink prop(默认 true),替代原本 HTML 注释掉 DocLink 的做法。 - sso-callback.vue: 等待提示改为 LottieLoading 动画。 Lottie 启动动画 - 新增 loading.html(注入进 index.html)+ public/loading.json + 运行时拷贝的 public/lottie_light.min.js(.gitignore 忽略)。 - 新增 public/lottie-tint.js:共享主题色适配器(LIGHTNESSES / SHADE_MAP / hslToRgb / patchColors / readPrimary),同时被启动白屏脚本与 Vue 组件使用, 消除两份几乎一致的实现。 - 新增 src/components/lottie-loading/LottieLoading.vue:按需加载 lottie-tint.js 并根据 CSS 变量 --primary 重新上色。 - vite.config.mts 新增 copyLottiePlayer 插件:configResolved 时无条件把 node_modules/lottie-web/.../lottie_light.min.js 拷到 public/,避免 mtime 误判。 - package.json 新增 lottie-web ^5.13.0 依赖。 i18n - 新增 otherLoginMethods / contactSupport / weComLogin 三个文案(zh-CN / en-US)。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,12 @@ import { useRoute } from 'vue-router';
|
||||
|
||||
import { AuthenticationLogin, Verification, z } from '@vben/common-ui';
|
||||
import { isCaptchaEnable, isTenantEnable } from '@vben/hooks';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { $t } from '@vben/locales';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
checkCaptcha,
|
||||
getCaptcha,
|
||||
@@ -96,6 +99,12 @@ async function handleThirdLogin(type: number) {
|
||||
if (type <= 0) {
|
||||
return;
|
||||
}
|
||||
// 多租户模式下,必须先选择租户;触发表单校验以提示用户
|
||||
if (tenantEnable && !accessStore.tenantId) {
|
||||
message.warning($t('authentication.tenantTip'));
|
||||
loginRef.value?.getFormApi().validateField('tenantId');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 计算 redirectUri
|
||||
// tricky: type、redirect 需要先 encode 一次,否则钉钉回调会丢失。配合 social-login.vue#getUrlValue() 使用
|
||||
@@ -170,17 +179,71 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="custom-login-wrapper">
|
||||
<AuthenticationLogin
|
||||
ref="loginRef"
|
||||
:form-schema="formSchema"
|
||||
:loading="authStore.loginLoading"
|
||||
:show-code-login="false"
|
||||
:show-doc-link="false"
|
||||
:show-qrcode-login="false"
|
||||
:show-register="false"
|
||||
:show-third-party-login="false"
|
||||
@submit="handleLogin"
|
||||
@third-login="handleThirdLogin"
|
||||
/>
|
||||
|
||||
<!-- 自定义其他登录方式 -->
|
||||
<div class="mt-8">
|
||||
<div class="relative mb-6 flex justify-center text-xs text-slate-400">
|
||||
<span
|
||||
class="relative z-10 bg-background px-3 font-medium dark:bg-slate-900"
|
||||
>
|
||||
{{ $t('authentication.otherLoginMethods') }}
|
||||
</span>
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div
|
||||
class="w-full border-t border-slate-100 dark:border-slate-700"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center gap-8">
|
||||
<!-- 手机登录 -->
|
||||
<button
|
||||
class="flex size-12 items-center justify-center rounded-2xl border border-slate-100 bg-slate-50 text-slate-400 transition-all hover:scale-110 hover:bg-white hover:text-slate-600 hover:shadow-md dark:border-slate-700 dark:bg-slate-800 dark:hover:bg-slate-700"
|
||||
:title="$t('authentication.mobileLogin')"
|
||||
type="button"
|
||||
@click="$router.push('/auth/code-login')"
|
||||
>
|
||||
<IconifyIcon icon="lucide:smartphone" class="size-5" />
|
||||
</button>
|
||||
|
||||
<!-- 二维码登录 -->
|
||||
<button
|
||||
class="flex size-12 items-center justify-center rounded-2xl border border-slate-100 bg-slate-50 text-slate-400 transition-all hover:scale-110 hover:bg-white hover:text-slate-600 hover:shadow-md dark:border-slate-700 dark:bg-slate-800 dark:hover:bg-slate-700"
|
||||
:title="$t('authentication.qrcodeLogin')"
|
||||
type="button"
|
||||
@click="$router.push('/auth/qrcode-login')"
|
||||
>
|
||||
<IconifyIcon icon="lucide:qr-code" class="size-6" />
|
||||
</button>
|
||||
|
||||
<!-- 企业微信扫码登录(社交登录 type=30) -->
|
||||
<button
|
||||
class="flex size-12 items-center justify-center rounded-2xl border border-slate-100 bg-slate-50 text-slate-400 transition-all hover:scale-110 hover:bg-white hover:text-[#2BAD13] hover:shadow-md dark:border-slate-700 dark:bg-slate-800 dark:hover:bg-slate-700"
|
||||
:title="$t('authentication.weComLogin')"
|
||||
type="button"
|
||||
@click="handleThirdLogin(30)"
|
||||
>
|
||||
<IconifyIcon icon="simple-icons:wechat" class="size-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Verification
|
||||
ref="verifyRef"
|
||||
v-if="captchaEnable"
|
||||
ref="verifyRef"
|
||||
:captcha-type="captchaType"
|
||||
:check-captcha-api="checkCaptcha"
|
||||
:get-captcha-api="getCaptcha"
|
||||
@@ -190,3 +253,10 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 确保表单容器宽度 */
|
||||
.custom-login-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { LOGIN_PATH } from '@vben/constants';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ssoCallback } from '#/api/core/sso';
|
||||
import { LottieLoading } from '#/components/lottie-loading';
|
||||
import {
|
||||
IOT_CLIENT_ID,
|
||||
SSO_CALLBACK_PATH,
|
||||
@@ -83,8 +84,8 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-screen items-center justify-center text-base">
|
||||
<span v-if="errorMsg">{{ errorMsg }}</span>
|
||||
<span v-else>正在登录,请稍候...</span>
|
||||
<div class="flex h-screen items-center justify-center">
|
||||
<div v-if="errorMsg" class="text-base">{{ errorMsg }}</div>
|
||||
<LottieLoading v-else :size="220" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user