mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-07 05:02:45 +08:00
fix: 消息中心纯文本换行丢失、登录页优化、底部导航液态玻璃效果
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "moviepilot",
|
||||
"version": "2.9.25",
|
||||
"version": "2.9.26",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"bin": "dist/service.js",
|
||||
|
||||
@@ -24,6 +24,7 @@ const imageLoadError = ref(false)
|
||||
// 初始化 markdown-it
|
||||
const md = new MarkdownIt({
|
||||
html: true,
|
||||
breaks: true,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
})
|
||||
|
||||
@@ -171,7 +171,7 @@ const showDynamicButton = computed(() => {
|
||||
<Teleport v-if="appMode && showNav" to="body">
|
||||
<div class="footer-nav-container">
|
||||
<TransitionGroup name="footer-nav" tag="div" class="footer-nav-group">
|
||||
<VCard key="main-nav" elevation="3" class="footer-nav-card border" rounded="pill">
|
||||
<VCard key="main-nav" :elevation="0" class="footer-nav-card" rounded="pill">
|
||||
<VCardText class="footer-card-content">
|
||||
<!-- 添加指示器 -->
|
||||
<div ref="indicator" class="nav-indicator"></div>
|
||||
@@ -217,8 +217,8 @@ const showDynamicButton = computed(() => {
|
||||
<VCard
|
||||
v-if="showDynamicButton"
|
||||
key="dynamic-btn"
|
||||
elevation="3"
|
||||
class="footer-nav-card dynamic-btn-card border"
|
||||
:elevation="0"
|
||||
class="footer-nav-card dynamic-btn-card"
|
||||
rounded="pill"
|
||||
>
|
||||
<VCardText class="footer-card-content">
|
||||
@@ -260,23 +260,120 @@ const showDynamicButton = computed(() => {
|
||||
|
||||
// 按钮卡片之间的间距
|
||||
> .v-card + .v-card {
|
||||
margin-inline-start: 2px; // 减少间距
|
||||
margin-inline-start: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-nav-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
backdrop-filter: blur(24px);
|
||||
background-color: rgba(var(--v-theme-surface), 0.6);
|
||||
pointer-events: auto;
|
||||
transition: all 0.5s cubic-bezier(0.25, 1, 0.5, 1);
|
||||
will-change: transform, max-width, opacity;
|
||||
|
||||
// ===== iOS 26 Liquid Glass 效果 =====
|
||||
|
||||
// 强力毛玻璃背景模糊
|
||||
backdrop-filter: blur(40px) saturate(1.8) brightness(1.05);
|
||||
-webkit-backdrop-filter: blur(40px) saturate(1.8) brightness(1.05);
|
||||
|
||||
// 半透明玻璃底色 - 浅色模式
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 255, 255, 0.45) 0%,
|
||||
rgba(255, 255, 255, 0.25) 40%,
|
||||
rgba(255, 255, 255, 0.3) 100%
|
||||
);
|
||||
|
||||
// 多层内发光边框 - 模拟玻璃边缘折射
|
||||
border: 0.5px solid rgba(255, 255, 255, 0.5) !important;
|
||||
box-shadow:
|
||||
// 外层柔和投影
|
||||
0 8px 32px rgba(0, 0, 0, 0.08),
|
||||
0 2px 12px rgba(0, 0, 0, 0.04),
|
||||
// 内发光 - 上边缘高光
|
||||
inset 0 1px 1px rgba(255, 255, 255, 0.6),
|
||||
// 内发光 - 整体光泽
|
||||
inset 0 0 20px rgba(255, 255, 255, 0.1);
|
||||
|
||||
// 顶部光泽反射条
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset-block-start: 0;
|
||||
inset-inline-start: 10%;
|
||||
z-index: 1;
|
||||
border-radius: 0 0 50% 50%;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0.5) 0%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
block-size: 40%;
|
||||
inline-size: 80%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// 底部微光
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset-block-end: 0;
|
||||
inset-inline-start: 5%;
|
||||
z-index: 1;
|
||||
border-radius: 50% 50% 0 0;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(255, 255, 255, 0.08) 0%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
block-size: 30%;
|
||||
inline-size: 90%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// ===== 暗色模式适配 =====
|
||||
.v-theme--dark & {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 255, 255, 0.12) 0%,
|
||||
rgba(255, 255, 255, 0.05) 40%,
|
||||
rgba(255, 255, 255, 0.08) 100%
|
||||
);
|
||||
border: 0.5px solid rgba(255, 255, 255, 0.15) !important;
|
||||
box-shadow:
|
||||
0 8px 32px rgba(0, 0, 0, 0.3),
|
||||
0 2px 12px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 1px rgba(255, 255, 255, 0.15),
|
||||
inset 0 0 20px rgba(255, 255, 255, 0.03);
|
||||
|
||||
&::before {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0.12) 0%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(255, 255, 255, 0.03) 0%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 透明主题下的特殊样式
|
||||
.v-theme--transparent & {
|
||||
backdrop-filter: blur(var(--transparent-blur-heavy, 16px));
|
||||
background-color: rgba(var(--v-theme-surface), var(--transparent-opacity-heavy, 0.5));
|
||||
backdrop-filter: blur(40px) saturate(1.8) brightness(1.05);
|
||||
-webkit-backdrop-filter: blur(40px) saturate(1.8) brightness(1.05);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 255, 255, 0.2) 0%,
|
||||
rgba(255, 255, 255, 0.08) 40%,
|
||||
rgba(255, 255, 255, 0.12) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.v-btn-toggle {
|
||||
@@ -287,6 +384,7 @@ const showDynamicButton = computed(() => {
|
||||
|
||||
.footer-card-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding-block: 4px;
|
||||
padding-inline: 6px;
|
||||
}
|
||||
@@ -309,12 +407,46 @@ const showDynamicButton = computed(() => {
|
||||
justify-content: center;
|
||||
background-color: transparent;
|
||||
block-size: 48px;
|
||||
transition: all 0.35s cubic-bezier(0.25, 1, 0.5, 1);
|
||||
|
||||
&.v-btn--active {
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
// 选中态 - 液态玻璃胶囊高亮
|
||||
&.footer-nav-btn-active {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 4px 2px;
|
||||
z-index: -1;
|
||||
border: 0.5px solid rgba(255, 255, 255, 0.35);
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(var(--v-theme-primary), 0.18) 0%,
|
||||
rgba(var(--v-theme-primary), 0.08) 100%
|
||||
);
|
||||
box-shadow:
|
||||
0 2px 8px rgba(var(--v-theme-primary), 0.15),
|
||||
inset 0 1px 1px rgba(255, 255, 255, 0.3);
|
||||
transition: all 0.35s cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
|
||||
.v-theme--dark &::before {
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(var(--v-theme-primary), 0.25) 0%,
|
||||
rgba(var(--v-theme-primary), 0.1) 100%
|
||||
);
|
||||
box-shadow:
|
||||
0 2px 8px rgba(var(--v-theme-primary), 0.2),
|
||||
inset 0 1px 1px rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -258,6 +258,7 @@ export default {
|
||||
serverError: 'Login failed, server error!',
|
||||
loginFailed: 'Login Failed',
|
||||
secondaryVerification: 'Secondary Verification',
|
||||
orDivider: 'OR',
|
||||
loginWithPasskey: 'Login with Passkey',
|
||||
loginWithOtp: 'Login with OTP',
|
||||
orUsePasskey: 'Or use Passkey for verification',
|
||||
|
||||
@@ -257,6 +257,7 @@ export default {
|
||||
serverError: '登录失败,服务器错误!',
|
||||
loginFailed: '登录失败',
|
||||
secondaryVerification: '二次验证',
|
||||
orDivider: '或',
|
||||
loginWithPasskey: '使用通行密钥登录',
|
||||
loginWithOtp: '使用验证码登录',
|
||||
orUsePasskey: '或使用通行密钥进行验证',
|
||||
|
||||
@@ -257,6 +257,7 @@ export default {
|
||||
noPermission: '登錄失敗,您沒有任何功能權限,請聯繫管理員!',
|
||||
loginFailed: '登錄失敗',
|
||||
secondaryVerification: '二次驗證',
|
||||
orDivider: '或',
|
||||
loginWithPasskey: '使用通行密鑰登錄',
|
||||
loginWithOtp: '使用驗證碼登錄',
|
||||
orUsePasskey: '或使用通行密鑰進行驗證',
|
||||
|
||||
@@ -610,15 +610,21 @@ onUnmounted(() => {
|
||||
</VCol>
|
||||
<VCol cols="12">
|
||||
<!-- login button -->
|
||||
<VBtn block type="submit" prepend-icon="mdi-login" :loading="loading">
|
||||
<VBtn block type="submit" prepend-icon="mdi-login" :loading="loading" size="large">
|
||||
{{ t('login.login') }}
|
||||
</VBtn>
|
||||
|
||||
<!-- or divider -->
|
||||
<div class="or-divider my-4">
|
||||
<span class="or-divider-text">{{ t('login.orDivider') }}</span>
|
||||
</div>
|
||||
|
||||
<!-- passkey login button -->
|
||||
<VBtn
|
||||
block
|
||||
variant="tonal"
|
||||
variant="outlined"
|
||||
color="success"
|
||||
class="mt-3 passkey-btn"
|
||||
class="passkey-btn"
|
||||
prepend-icon="material-symbols:passkey"
|
||||
:loading="passkeyLoading"
|
||||
@click="loginWithPassKey(false)"
|
||||
@@ -718,8 +724,29 @@ onUnmounted(() => {
|
||||
background: rgba(var(--v-theme-surface), 0.7) !important;
|
||||
}
|
||||
|
||||
.or-divider {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
border-block-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
|
||||
}
|
||||
|
||||
.or-divider-text {
|
||||
padding-inline: 12px;
|
||||
font-size: 0.8125rem;
|
||||
color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.v-theme--light {
|
||||
.passkey-btn.v-btn--variant-tonal {
|
||||
.passkey-btn.v-btn--variant-outlined {
|
||||
color: rgb(86, 170, 0) !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user