style: 修改一些UI和UE,提升用户体验

This commit is contained in:
nap0o committed 2026-03-13 13:24:00 +08:00
1 parent 1c1d34df02
commit c634f0727e
11 files changed
+67 -47

No files matched your search

+6 -11
View File
@@ -38,40 +38,35 @@ const router = createRouter({
})
// 路由守卫:拦截未登录用户的访问
router.beforeEach(async (to, from, next) => {
router.beforeEach(async (to) => {
const authUserStore = useAuthUserStore()
// 1. 检查内存中是否有用户信息 (乐观检查)
let isAuthenticated = !!(authUserStore.userInfo && authUserStore.userInfo.id)
// 2. 如果内存无状态,但目标页需要登录 OR 是仅游客页面(如登录页),尝试通过 Cookie 恢复会话
// (Option 1B: 即使访问登录页,也先确认是否已登录,防止误判)
if (!isAuthenticated && (to.meta.requiresAuth || to.meta.guestOnly)) {
try {
await authUserStore.fetchUserInfo()
isAuthenticated = !!(authUserStore.userInfo && authUserStore.userInfo.id)
} catch (e) {
// Handle the case where the application environment is unstable/not healthy
if (e.isSecurity || e.message === 'Security Check Failed') {
next('/health')
return
return '/health'
}
}
}
// 3. 处理需要登录的页面
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login')
return
return '/login'
}
// 4. 处理仅限游客的页面 (Option 2B: 已登录用户访问登录页则跳转首页)
// 4. 处理仅限游客的页面
if (to.meta.guestOnly && isAuthenticated) {
next('/')
return
return '/'
}
next()
return true
})
export default router
@@ -148,7 +148,7 @@
display: flex;
flex-direction: column;
gap: 16px;
padding: 20px 20px 0;
padding: 20px;
background-color: none;
border: 1px solid var(--el-border-color-lighter);
border-radius: 12px;
@@ -210,14 +210,14 @@
background-color: transparent;
}
.ecosystem-collapse :deep(.el-collapse),
.ecosystem-collapse :deep(.el-collapse-item),
.ecosystem-collapse :deep(.el-collapse-item__header),
.ecosystem-collapse :deep(.el-collapse-item__wrap) {
.ecosystem-collapse,
.ecosystem-collapse .el-collapse-item,
.ecosystem-collapse .el-collapse-item__header,
.ecosystem-collapse .el-collapse-item__wrap {
border: none !important;
}
.ecosystem-collapse :deep(.el-collapse-item__header) {
.ecosystem-collapse .el-collapse-item__header {
background-color: transparent;
padding: 0;
font-size: 13px;
@@ -226,11 +226,11 @@
line-height: 32px;
}
.ecosystem-collapse :deep(.el-collapse-item__wrap) {
.ecosystem-collapse .el-collapse-item__wrap {
background-color: transparent;
}
.ecosystem-collapse :deep(.el-collapse-item__content) {
.ecosystem-collapse .el-collapse-item__content {
padding: 0;
}
@@ -321,6 +321,6 @@ html.dark .ecosystem-item:hover {
padding: 10px;
}
.migration-tabs :deep(.el-tabs__nav-wrap::after) {
.migration-tabs .el-tabs__nav-wrap::after {
height: 1px;
}
@@ -99,11 +99,6 @@
transform: translateY(-50%);
}
html.dark .global-offline-banner {
background-color: rgba(230, 162, 60, 0.1);
border-bottom-color: rgba(230, 162, 60, 0.2);
}
/* 3. QrScanner Component */
.qr-scanner-container {
width: 100%;
@@ -262,7 +262,7 @@
/* AddVaultManual.vue */
.vault-manual-form-wrapper {
padding: 10px 0;
padding: 0;
}
.vault-manual-submit-btn {
@@ -242,7 +242,14 @@ const handleDelete = async (row) => {
const formatDate = (timestamp) => {
if (!timestamp) return '-'
return new Date(timestamp).toLocaleString()
const date = new Date(timestamp)
const Y = date.getFullYear()
const M = String(date.getMonth() + 1).padStart(2, '0')
const D = String(date.getDate()).padStart(2, '0')
const h = String(date.getHours()).padStart(2, '0')
const m = String(date.getMinutes()).padStart(2, '0')
const s = String(date.getSeconds()).padStart(2, '0')
return `${Y}-${M}-${D} ${h}:${m}:${s}`
}
onMounted(() => {
@@ -39,7 +39,7 @@
<div class="card-content">
<p class="backup-status-text">
{{ $t('backup.last_backup') }}
<span v-if="provider.lastBackupAt">{{ new Date(provider.lastBackupAt).toLocaleString() }}</span>
<span v-if="provider.lastBackupAt">{{ formatDate(provider.lastBackupAt) }}</span>
<span v-else>{{ $t('backup.never_backed_up') }}</span>
<el-tag v-if="provider.lastBackupStatus" size="small" :type="provider.lastBackupStatus === 'success' ? 'success' : 'danger'" class="ml-5">
{{ provider.lastBackupStatus }}
@@ -513,7 +513,11 @@
{{ formatSize(scope.row.size) }}
</template>
</el-table-column>
<el-table-column prop="lastModified" :label="$t('backup.time')" width="180" />
<el-table-column :label="$t('backup.time')" width="180">
<template #default="scope">
{{ formatDate(scope.row.lastModified) }}
</template>
</el-table-column>
<el-table-column :label="$t('backup.action')" width="100">
<template #default="scope">
<el-button link type="primary" @click="selectRestoreFile(scope.row)">{{ $t('backup.restore') }}</el-button>
@@ -597,14 +601,14 @@ const handleSmtpSecureChange = (val) => {
const getProviderTypeTag = (type) => {
const map = {
webdav: 'primary',
webdav: 'danger',
s3: 'warning',
telegram: 'success',
gdrive: 'danger',
onedrive: 'success',
baidu: 'warning',
telegram: 'primary',
gdrive: 'success',
onedrive: 'primary',
baidu: 'primary',
dropbox: 'primary',
email: 'info'
email: 'success'
}
return map[type] || 'info'
}
@@ -616,4 +620,16 @@ const formatSize = (bytes) => {
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
const formatDate = (dateStr) => {
if (!dateStr) return '-'
const date = new Date(dateStr)
const Y = date.getFullYear()
const M = String(date.getMonth() + 1).padStart(2, '0')
const D = String(date.getDate()).padStart(2, '0')
const h = String(date.getHours()).padStart(2, '0')
const m = String(date.getMinutes()).padStart(2, '0')
const s = String(date.getSeconds()).padStart(2, '0')
return `${Y}-${M}-${D} ${h}:${m}:${s}`
}
</script>
@@ -64,9 +64,9 @@
<el-collapse class="ecosystem-collapse">
<el-collapse-item name="1">
<template #title>
<h4 class="group-title">
<el-icon class="mr-8"><QuestionFilled /></el-icon> <strong>{{ $t('migration.ga_ms_import_guide') }}</strong>
</h4>
<div class="migration-guide">
<strong>{{ $t('migration.ga_ms_import_guide') }}</strong>
</div>
</template>
<div class="migration-ga-tip">
<span><el-icon><iconGoogleAuth /></el-icon> Google Authenticator</span>
@@ -49,7 +49,7 @@
<div class="sidebar-footer" :class="{ 'is-collapsed': isCollapse }">
<el-button circle :icon="isCollapse ? Expand : Fold" @click="toggleCollapse" :title="isCollapse ? '展开' : '折叠'" />
<el-button circle :icon="themeStore.isDark ? Sunny : Moon" @click="themeStore.toggleTheme" />
<el-button circle size="medium" :icon="iconLocales" :title="$i18n.locale === 'zh-CN' ? 'English' : '切换语言'" @click="toggleLanguage" />
<el-button circle size="default" :icon="iconLocales" :title="$i18n.locale === 'zh-CN' ? 'English' : '切换语言'" @click="toggleLanguage" />
<el-button circle @click="handleLogout" :title="$t('menu.logout')">
<el-icon><SwitchButton /></el-icon>
</el-button>
@@ -128,9 +128,9 @@
</el-menu>
<div class="sidebar-footer" style="display: flex; gap: 15px; justify-content: center; padding: 20px 0;">
<el-button circle size="medium" :icon="themeStore.isDark ? Sunny : Moon" @click="themeStore.toggleTheme" />
<el-button circle size="medium" :icon="iconLocales" :title="$i18n.locale === 'zh-CN' ? 'English' : '切换语言'" @click="toggleLanguage" />
<el-button circle size="medium" @click="handleLogout" :title="$t('menu.logout')">
<el-button circle size="default" :icon="themeStore.isDark ? Sunny : Moon" @click="themeStore.toggleTheme" />
<el-button circle size="default" :icon="iconLocales" :title="$i18n.locale === 'zh-CN' ? 'English' : '切换语言'" @click="toggleLanguage" />
<el-button circle size="default" @click="handleLogout" :title="$t('menu.logout')">
<el-icon><SwitchButton /></el-icon>
</el-button>
</div>
@@ -170,7 +170,7 @@ const themeStore = useThemeStore()
const authUserStore = useAuthUserStore()
const router = useRouter()
const isCollapse = ref(localStorage.getItem('sidebar_collapse') === 'true')
const isCollapse = ref(localStorage.getItem('sidebar_collapse') !== 'false')
const toggleCollapse = () => {
isCollapse.value = !isCollapse.value
+2 -2
View File
@@ -41,7 +41,7 @@
"backup": "Cloud Backup",
"migration": "Migration",
"tools": "Toolbox",
"security": "PassKey",
"security": "Passkey",
"settings": "Settings",
"logout": "Sign Out"
},
@@ -733,7 +733,7 @@
"connection_failed": "Cannot connect to server for health check"
},
"security": {
"management": "Passkey Management",
"management": "Passkey List",
"passkey_title": "Passkey Management",
"passkey_desc": "Enable passwordless login using biometrics such as fingerprint or face recognition for a more secure and convenient access experience.",
"passkey_list": "Registered Passkeys",
+1 -1
View File
@@ -734,7 +734,7 @@
},
"security": {
"management": "通行密钥管理",
"passkey_title": "通行密钥管理",
"passkey_title": "通行密钥",
"passkey_desc": "通过生物识别(如指纹或面容)实现无密码登录,为您提供更安全、更快速的账户访问体验。",
"passkey_list": "已绑定的通行密钥",
"add_passkey": "添加通行密钥",
+8 -1
View File
@@ -92,7 +92,14 @@ export default defineConfig({
clientsClaim: true,
skipWaiting: true,
navigateFallback: '/index.html',
navigateFallbackDenylist: [/^\/api/],
// 排除 API 和 Vite 开发环境下的内部路径,防止 Service Worker 拦截干扰
navigateFallbackDenylist: [
/^\/api/,
/\/@vite\//,
/\/@fs\//,
/\/@id\//,
/node_modules/
],
runtimeCaching: [
{
urlPattern: ({ url }) => url.pathname.startsWith('/api'),