chore: 完成多批次代码优化与重构

- 重构工作流模块目录结构,迁移代码文件
- 修复类型断言空值安全问题,添加 ! 操作符
- 优化样式类名,替换 flex-cc 为标准 flex 工具类
- 更新路由标签简化文案,移除冗余注释
- 调整 ruff 配置,放宽行长度限制
- 更新 README 与多语言文案,优化项目描述
- 修复表单、图表组件的类型与样式问题
- 简化搜索表单、数据卡片的布局代码
This commit is contained in:
zhangtao
2026-06-20 05:31:46 +08:00
parent 82b742620b
commit d34d4a4c50
378 changed files with 4149 additions and 6375 deletions
@@ -1,6 +1,6 @@
<template>
<div class="page-content border-0! bg-transparent! min-h-screen flex-cc">
<div class="flex-cc max-md:block! max-md:text-center">
<div class="page-content border-0! bg-transparent! min-h-screen flex items-center justify-center">
<div class="flex items-center justify-center max-md:block! max-md:text-center">
<FaThemeSvg :src="data.imgUrl" size="100%" class="w-100!" />
<div class="ml-15 w-75 max-md:mx-auto max-md:mt-10 max-md:w-full max-md:text-center">
<p class="text-xl leading-7 text-g-600 max-md:text-lg">{{ data.desc }}</p>
@@ -0,0 +1,442 @@
/**
* 登录根布局
* - 顶栏 fixed 透明叠在页面上(不占一条实体顶区);仅插画列 / 表单区随布局切换
* - left / right:插画列与表单列横向排列(约 65% / 35%)
* - center:全宽 + 视口级背景;登录卡居中、阴影高亮
*/
.login-auth-split {
display: flex;
min-width: 0;
min-height: 0;
}
.login-auth-split__col--illustration {
box-sizing: border-box;
min-width: 0;
min-height: 0;
}
.login-auth-split__col--form {
box-sizing: border-box;
min-width: 0;
min-height: 0;
}
/* 横向:插画 ~65%,表单列占剩余 */
.login-auth-split--left,
.login-auth-split--right {
flex-direction: row;
}
.login-auth-split--left .login-auth-split__col--illustration,
.login-auth-split--right .login-auth-split__col--illustration {
flex: 0 0 65%;
}
.login-auth-split--left .login-auth-split__col--form,
.login-auth-split--right .login-auth-split__col--form {
flex: 1 1 0;
min-width: 0;
}
.login-auth-split--left {
flex-direction: row-reverse;
}
@media only screen and (width <= 1600px) {
.login-auth-split--left .login-auth-split__col--illustration,
.login-auth-split--right .login-auth-split__col--illustration {
flex: 0 0 60%;
}
}
/* 居中模式:仅一列表单壳占满屏宽 */
.login-auth-split--center {
flex-direction: column;
}
.login-auth-split--center .login-auth-split__col--form {
flex: 1 1 auto;
width: 100%;
min-height: 0;
background-color: transparent;
}
/* 顶栏与页脚之间的区域:纵向居中承载登录卡 */
.login-auth-split--center .login-page-panel__scroll.login-page-panel__scroll--centered {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 0;
padding-top: 1.5rem;
padding-bottom: 1.5rem;
/* 盖过 Tailwind overflow-y-auto:仅卡片内滚动,外层不出滚动条 */
overflow: hidden;
}
.login-page-panel__scroll--centered .login-panel-align-row {
flex: 0 1 auto;
justify-content: center;
width: 100%;
min-height: 0;
max-height: 100%;
}
/* 悬浮卡片:玻璃拟态,圆角透明 */
.login-auth-split--center .auth-right-wrap {
width: min(480px, calc(100vw - 2rem)) !important;
max-width: min(480px, calc(100vw - 2rem));
height: auto;
max-height: calc(100dvh - 8rem);
padding: 1.25rem 1.75rem;
overflow: hidden auto;
background: color-mix(in srgb, var(--el-bg-color) 72%, transparent);
border: 1px solid color-mix(in srgb, var(--el-border-color-lighter) 60%, transparent);
border-radius: 20px;
box-shadow:
0 1px 8px rgb(15 23 42 / 5%),
0 0 16px color-mix(in srgb, var(--el-color-primary) 6%, transparent);
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
}
.login-auth-split--center .auth-right-wrap .form {
height: auto;
min-height: 0;
padding-top: 0;
padding-bottom: 0;
}
html.dark .login-auth-split--center .auth-right-wrap {
background: color-mix(in srgb, var(--el-bg-color) 55%, transparent);
border-color: rgb(255 255 255 / 8%);
box-shadow:
0 1px 8px rgb(0 0 0 / 18%),
0 0 16px color-mix(in srgb, var(--el-color-primary) 10%, transparent);
}
/* 居中模式下页脚弱化,减少「分区后台」感 */
.login-page-footer--floating-layout {
border-top-color: color-mix(in srgb, var(--el-border-color-lighter) 55%, transparent);
}
html.dark .login-page-footer--floating-layout {
border-top-color: rgb(255 255 255 / 8%);
}
/* 居中模式 footer 单行展示 */
.login-auth-split--center .login-page-footer--floating-layout {
.login-footer-text {
white-space: nowrap;
}
.login-footer-row {
display: inline;
}
.login-footer-row + .login-footer-row {
margin-top: 0;
}
.login-footer-sep-center {
display: inline;
}
}
@media only screen and (width <= 1180px) {
.login-auth-split {
flex-direction: column !important;
}
.login-auth-split__col--illustration {
flex: 0 0 auto !important;
width: 100%;
}
.login-auth-split__col--form {
flex: 1 1 auto !important;
width: 100%;
min-height: 0;
}
/* 窄屏横排切竖排:恢复插画;居中模式本身无插画,不受影响 */
.login-auth-split--center .auth-right-wrap {
width: min(420px, calc(100vw - 1.5rem)) !important;
max-width: min(420px, calc(100vw - 1.5rem));
max-height: calc(100dvh - 6rem);
}
}
/* 授权页表单区域(左右分栏时) */
.auth-right-wrap {
position: relative;
flex-shrink: 0;
width: 440px;
height: 670px;
padding-top: 5px;
padding-bottom: 5px;
overflow: hidden;
animation: slideInRight 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
@media (width <= 640px) {
width: 100%;
height: auto;
max-height: calc(100vh - 10rem);
padding-right: 1.75rem;
padding-left: 1.75rem;
}
@media (width <= 768px) {
animation: none;
}
.form {
height: 100%;
padding-top: 10px;
padding-bottom: 40px;
}
.title {
font-size: 2.25rem;
font-weight: 600;
line-height: 2.5rem;
color: var(--fa-gray-900);
@media (width <= 768px) {
font-size: 1.875rem;
line-height: 2.25rem;
}
@media (width <= 640px) {
padding-top: 2.5rem;
}
}
.sub-title {
margin-top: 10px;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--fa-gray-600);
}
.custom-height {
height: 40px !important;
}
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.login-page-panel {
.btn {
border-radius: 8px;
}
}
html.dark .login-page-panel {
.text-g-800 {
color: rgb(255 255 255 / 78%) !important;
}
}
.login-page-form {
margin-top: 1rem;
}
.login-page-form .el-form-item {
margin-bottom: 1.1rem;
}
.el-select__wrapper {
min-height: 42px !important;
}
.login-secondary-actions .login-secondary-btn {
height: 42px;
border-radius: 8px;
}
html.dark {
.login-page-form .el-input__wrapper {
background-color: rgb(255 255 255 / 6%) !important;
box-shadow: 0 0 0 1px rgb(255 255 255 / 10%) inset !important;
}
.login-page-form .el-input__inner {
color: rgb(255 255 255 / 92%);
}
.login-page-form .el-input__inner::placeholder {
color: rgb(255 255 255 / 35%);
}
.login-page-form .el-select .el-select__wrapper {
background-color: rgb(255 255 255 / 6%) !important;
box-shadow: 0 0 0 1px rgb(255 255 255 / 10%) inset !important;
}
.login-page-form .el-select__placeholder {
color: rgb(255 255 255 / 40%);
}
.login-page-form .el-select__selected-item {
color: rgb(255 255 255 / 92%);
}
.login-remember .el-checkbox__label {
color: rgb(255 255 255 / 65%);
}
.login-secondary-btn {
color: rgb(255 255 255 / 85%) !important;
background: transparent !important;
border-color: rgb(255 255 255 / 22%) !important;
border-radius: 8px;
&:hover {
color: var(--el-color-primary) !important;
background: var(--el-color-primary-light-9) !important;
border-color: var(--el-color-primary) !important;
}
}
.login-secondary-actions .login-secondary-btn {
height: 42px;
}
}
.login-page-footer {
margin-top: 0;
text-align: center;
border-top: 1px solid var(--el-border-color-lighter);
.login-footer-text {
line-height: 1.65;
color: var(--el-text-color-secondary) !important;
}
.login-footer-row {
white-space: nowrap;
}
.login-footer-row + .login-footer-row {
margin-top: 0.25rem;
}
.login-footer-sep-center {
display: none;
}
.login-page-footer__link {
color: var(--el-text-color-regular);
text-decoration: none;
transition: color 0.2s ease;
&:hover {
color: var(--el-color-primary);
}
}
.login-page-footer__sep {
margin: 0 0.35rem;
color: var(--el-border-color);
}
.login-page-footer__record {
display: inline-block;
color: var(--el-text-color-placeholder);
}
}
html.dark .login-page-footer {
border-top-color: rgb(255 255 255 / 8%);
.login-footer-text {
color: rgb(255 255 255 / 42%) !important;
}
.login-page-footer__link {
color: rgb(255 255 255 / 48%);
&:hover {
color: var(--el-color-primary);
}
}
.login-page-footer__sep {
color: rgb(255 255 255 / 25%);
}
.login-page-footer__record {
color: rgb(255 255 255 / 35%);
}
}
.login-mobile-otp-cell {
box-sizing: border-box;
flex: 1;
min-width: 0;
max-width: 48px;
height: 40px;
padding: 0;
font-size: 1rem;
font-weight: 500;
color: var(--el-text-color-primary);
text-align: center;
appearance: none;
outline: none;
background: var(--el-fill-color-blank);
border: 1px solid var(--el-border-color);
border-radius: 8px;
transition:
border-color 0.2s ease,
box-shadow 0.2s ease;
&:focus {
border-color: var(--el-color-primary);
box-shadow: 0 0 0 1px var(--el-color-primary) inset;
}
}
.login-mobile-sms-btn {
border-radius: 8px;
}
.login-mobile-actions {
.el-button + .el-button {
margin-left: 0 !important;
}
.el-button {
box-sizing: border-box;
width: 100%;
min-width: 0;
max-width: 100%;
}
}
.login-qr-card {
box-sizing: border-box;
}
html.dark {
.login-mobile-otp-cell {
background: rgb(255 255 255 / 6%);
border-color: rgb(255 255 255 / 14%);
&:focus {
border-color: var(--el-color-primary);
}
}
}
@@ -101,7 +101,7 @@
<div class="login-form-tail flex flex-col gap-[1.1rem]">
<div class="relative pb-3">
<div
class="relative z-2 overflow-hidden select-none rounded-lg border border-transparent tad-300"
class="relative z-2 overflow-hidden select-none rounded-lg border border-transparent transition duration-300"
:class="{ 'border-[#FF4E4F]!': !isPassing && isClickPass }"
>
<FaDragVerify
@@ -116,14 +116,14 @@
/>
</div>
<p
class="absolute top-0 z-1 mt-2 px-px text-xs text-[#f56c6c] tad-300"
class="absolute top-0 z-1 mt-2 px-px text-xs text-[#f56c6c] transition duration-300"
:class="{ 'translate-y-10': !isPassing && isClickPass }"
>
{{ $t("login.placeholder.slider") }}
</p>
</div>
<div class="login-options-row flex-cb text-sm">
<div class="login-options-row flex items-center justify-between text-sm">
<ElCheckbox v-model="loginForm.remember" class="login-remember">
{{ $t("login.rememberPwd") }}
</ElCheckbox>
@@ -231,5 +231,5 @@ defineExpose({
</script>
<style scoped lang="scss">
@use "@styles/custom/fa-login";
@use "../fa-login";
</style>
@@ -122,5 +122,5 @@ defineExpose({
</script>
<style scoped lang="scss">
@use "@styles/custom/fa-login";
@use "../fa-login";
</style>
@@ -203,5 +203,5 @@ onBeforeUnmount(() => {
</script>
<style scoped lang="scss">
@use "@styles/custom/fa-login";
@use "../fa-login";
</style>
@@ -72,5 +72,5 @@ const qrPayload = computed(() => {
</script>
<style scoped lang="scss">
@use "@styles/custom/fa-login";
@use "../fa-login";
</style>
@@ -144,5 +144,5 @@ defineExpose({
</script>
<style scoped lang="scss">
@use "@styles/custom/fa-login";
@use "../fa-login";
</style>
@@ -16,16 +16,16 @@
</div>
<div
class="auth-top-bar-actions-panel pointer-events-auto flex shrink-0 flex-cc gap-1.5 px-2 py-1.5 max-sm:mr-1"
class="auth-top-bar-actions-panel pointer-events-auto flex shrink-0 flex items-center justify-center gap-1.5 px-2 py-1.5 max-sm:mr-1"
>
<div class="color-picker-expandable relative flex-c max-sm:hidden!">
<div class="color-picker-expandable relative flex items-center max-sm:hidden!">
<div
class="color-dots absolute right-0 rounded-full flex-c gap-2 rounded-5 px-2.5 py-2 pr-9 pl-2.5 opacity-0"
class="color-dots absolute right-0 rounded-full flex items-center gap-2 rounded-5 px-2.5 py-2 pr-9 pl-2.5 opacity-0"
>
<div
v-for="(_color, index) in mainColors"
:key="_color"
class="color-dot relative size-5 c-p flex-cc rounded-full opacity-0"
class="color-dot relative size-5 cursor-pointer flex items-center justify-center rounded-full opacity-0"
:class="{ active: _color === systemThemeColor }"
:style="{ background: _color, '--index': index }"
@click="changeThemeColor(_color)"
@@ -33,7 +33,9 @@
<FaSvgIcon v-if="_color === systemThemeColor" icon="ri:check-fill" class="text-white" />
</div>
</div>
<div class="btn palette-btn auth-top-bar__action relative z-2 h-8 w-8 c-p flex-cc tad-300">
<div
class="btn palette-btn auth-top-bar__action relative z-2 h-8 w-8 cursor-pointer flex items-center justify-center transition duration-300"
>
<FaSvgIcon icon="ri:palette-line" class="text-xl transition-colors duration-300" />
</div>
</div>
@@ -43,7 +45,7 @@
popper-class="langDropDownStyle"
>
<div
class="btn layout-align-btn auth-top-bar__action h-8 w-8 c-p flex-cc tad-300"
class="btn layout-align-btn auth-top-bar__action h-8 w-8 cursor-pointer flex items-center justify-center transition duration-300"
:title="$t('login.panelAlign.label')"
>
<FaSvgIcon
@@ -71,7 +73,9 @@
@command="changeLanguage"
popper-class="langDropDownStyle"
>
<div class="btn language-btn auth-top-bar__action h-8 w-8 c-p flex-cc tad-300">
<div
class="btn language-btn auth-top-bar__action h-8 w-8 cursor-pointer flex items-center justify-center transition duration-300"
>
<FaSvgIcon
icon="ri:translate-2"
class="text-[19px] text-g-800 transition-colors duration-300"
@@ -93,7 +97,7 @@
</ElDropdown>
<div
v-if="shouldShowThemeToggle"
class="btn theme-btn auth-top-bar__action h-8 w-8 c-p flex-cc tad-300"
class="btn theme-btn auth-top-bar__action h-8 w-8 cursor-pointer flex items-center justify-center transition duration-300"
@click="themeAnimation"
>
<FaSvgIcon
@@ -115,7 +119,7 @@ import { themeAnimation } from "@utils";
import { languageOptions } from "@/locales";
import { LanguageEnum } from "@/enums/appEnum";
import AppConfig from "@/config";
import { LoginPanelAlign } from "@/components/views/fa-login/useLoginPanelAlign";
import { LoginPanelAlign } from "@/components/views/fa-login/composables/useLoginPanelAlign";
defineOptions({ name: "AuthTopBar" });