fix: 消息中心纯文本换行丢失、登录页优化、底部导航液态玻璃效果

This commit is contained in:
jxxghp
2026-04-07 13:10:19 +08:00
parent fe22403e66
commit e2e239f6d9
7 changed files with 176 additions and 13 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "moviepilot", "name": "moviepilot",
"version": "2.9.25", "version": "2.9.26",
"private": true, "private": true,
"type": "module", "type": "module",
"bin": "dist/service.js", "bin": "dist/service.js",
+1
View File
@@ -24,6 +24,7 @@ const imageLoadError = ref(false)
// 初始化 markdown-it // 初始化 markdown-it
const md = new MarkdownIt({ const md = new MarkdownIt({
html: true, html: true,
breaks: true,
linkify: true, linkify: true,
typographer: true, typographer: true,
}) })
+140 -8
View File
@@ -171,7 +171,7 @@ const showDynamicButton = computed(() => {
<Teleport v-if="appMode && showNav" to="body"> <Teleport v-if="appMode && showNav" to="body">
<div class="footer-nav-container"> <div class="footer-nav-container">
<TransitionGroup name="footer-nav" tag="div" class="footer-nav-group"> <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"> <VCardText class="footer-card-content">
<!-- 添加指示器 --> <!-- 添加指示器 -->
<div ref="indicator" class="nav-indicator"></div> <div ref="indicator" class="nav-indicator"></div>
@@ -217,8 +217,8 @@ const showDynamicButton = computed(() => {
<VCard <VCard
v-if="showDynamicButton" v-if="showDynamicButton"
key="dynamic-btn" key="dynamic-btn"
elevation="3" :elevation="0"
class="footer-nav-card dynamic-btn-card border" class="footer-nav-card dynamic-btn-card"
rounded="pill" rounded="pill"
> >
<VCardText class="footer-card-content"> <VCardText class="footer-card-content">
@@ -260,23 +260,120 @@ const showDynamicButton = computed(() => {
// 按钮卡片之间的间距 // 按钮卡片之间的间距
> .v-card + .v-card { > .v-card + .v-card {
margin-inline-start: 2px; // 减少间距 margin-inline-start: 4px;
} }
} }
.footer-nav-card { .footer-nav-card {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
backdrop-filter: blur(24px);
background-color: rgba(var(--v-theme-surface), 0.6);
pointer-events: auto; pointer-events: auto;
transition: all 0.5s cubic-bezier(0.25, 1, 0.5, 1); transition: all 0.5s cubic-bezier(0.25, 1, 0.5, 1);
will-change: transform, max-width, opacity; 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 & { .v-theme--transparent & {
backdrop-filter: blur(var(--transparent-blur-heavy, 16px)); backdrop-filter: blur(40px) saturate(1.8) brightness(1.05);
background-color: rgba(var(--v-theme-surface), var(--transparent-opacity-heavy, 0.5)); -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 { .v-btn-toggle {
@@ -287,6 +384,7 @@ const showDynamicButton = computed(() => {
.footer-card-content { .footer-card-content {
position: relative; position: relative;
z-index: 2;
padding-block: 4px; padding-block: 4px;
padding-inline: 6px; padding-inline: 6px;
} }
@@ -309,12 +407,46 @@ const showDynamicButton = computed(() => {
justify-content: center; justify-content: center;
background-color: transparent; background-color: transparent;
block-size: 48px; block-size: 48px;
transition: all 0.35s cubic-bezier(0.25, 1, 0.5, 1);
&.v-btn--active { &.v-btn--active {
background-color: transparent; background-color: transparent;
box-shadow: none; 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 { .btn-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
+1
View File
@@ -258,6 +258,7 @@ export default {
serverError: 'Login failed, server error!', serverError: 'Login failed, server error!',
loginFailed: 'Login Failed', loginFailed: 'Login Failed',
secondaryVerification: 'Secondary Verification', secondaryVerification: 'Secondary Verification',
orDivider: 'OR',
loginWithPasskey: 'Login with Passkey', loginWithPasskey: 'Login with Passkey',
loginWithOtp: 'Login with OTP', loginWithOtp: 'Login with OTP',
orUsePasskey: 'Or use Passkey for verification', orUsePasskey: 'Or use Passkey for verification',
+1
View File
@@ -257,6 +257,7 @@ export default {
serverError: '登录失败,服务器错误!', serverError: '登录失败,服务器错误!',
loginFailed: '登录失败', loginFailed: '登录失败',
secondaryVerification: '二次验证', secondaryVerification: '二次验证',
orDivider: '或',
loginWithPasskey: '使用通行密钥登录', loginWithPasskey: '使用通行密钥登录',
loginWithOtp: '使用验证码登录', loginWithOtp: '使用验证码登录',
orUsePasskey: '或使用通行密钥进行验证', orUsePasskey: '或使用通行密钥进行验证',
+1
View File
@@ -257,6 +257,7 @@ export default {
noPermission: '登錄失敗,您沒有任何功能權限,請聯繫管理員!', noPermission: '登錄失敗,您沒有任何功能權限,請聯繫管理員!',
loginFailed: '登錄失敗', loginFailed: '登錄失敗',
secondaryVerification: '二次驗證', secondaryVerification: '二次驗證',
orDivider: '或',
loginWithPasskey: '使用通行密鑰登錄', loginWithPasskey: '使用通行密鑰登錄',
loginWithOtp: '使用驗證碼登錄', loginWithOtp: '使用驗證碼登錄',
orUsePasskey: '或使用通行密鑰進行驗證', orUsePasskey: '或使用通行密鑰進行驗證',
+31 -4
View File
@@ -610,15 +610,21 @@ onUnmounted(() => {
</VCol> </VCol>
<VCol cols="12"> <VCol cols="12">
<!-- login button --> <!-- 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') }} {{ t('login.login') }}
</VBtn> </VBtn>
<!-- or divider -->
<div class="or-divider my-4">
<span class="or-divider-text">{{ t('login.orDivider') }}</span>
</div>
<!-- passkey login button --> <!-- passkey login button -->
<VBtn <VBtn
block block
variant="tonal" variant="outlined"
color="success" color="success"
class="mt-3 passkey-btn" class="passkey-btn"
prepend-icon="material-symbols:passkey" prepend-icon="material-symbols:passkey"
:loading="passkeyLoading" :loading="passkeyLoading"
@click="loginWithPassKey(false)" @click="loginWithPassKey(false)"
@@ -718,8 +724,29 @@ onUnmounted(() => {
background: rgba(var(--v-theme-surface), 0.7) !important; 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 { .v-theme--light {
.passkey-btn.v-btn--variant-tonal { .passkey-btn.v-btn--variant-outlined {
color: rgb(86, 170, 0) !important; color: rgb(86, 170, 0) !important;
} }
} }