|
<script setup lang="ts"> |
|
import { useRoute, useRouter } from 'vue-router'; |
|
import { ref } from 'vue'; |
|
import logo from './assets/logo.png' |
|
const router = useRouter(); |
|
const route = useRoute(); |
|
const menuVisible = ref(false); |
|
|
|
const menu = [ |
|
{ |
|
name: '仓库', |
|
path: '/repo', |
|
icon: 'system-log' |
|
}, |
|
{ |
|
name: '账号', |
|
path: '/account', |
|
icon: 'user-list' |
|
}, |
|
{ |
|
name: '设置', |
|
path: '/setting', |
|
icon: 'setting-1' |
|
} |
|
]; |
|
|
|
const logout = () => { |
|
window.localStorage.removeItem('isAuthenticated'); |
|
router.push('/login'); |
|
}; |
|
|
|
const toggleMenu = () => { |
|
menuVisible.value = !menuVisible.value; |
|
}; |
|
|
|
|
|
|
|
const parseToken = () => { |
|
try { |
|
const token = localStorage.getItem('token') as string; |
|
const [, payload] = token.split('.'); |
|
const data = JSON.parse(atob(payload)); |
|
return data; |
|
} catch (error) { |
|
return null; |
|
} |
|
}; |
|
const jwtToken = parseToken(); |
|
|
|
</script> |
|
|
|
<template> |
|
<template v-if="route.path === '/login'"> |
|
<router-view /> |
|
</template> |
|
<t-layout v-else class="h-screen"> |
|
|
|
<t-drawer v-model:visible="menuVisible" placement="left" size="232" :footer="false" :header="false" |
|
:close-on-overlay-click="true" class="lg:hidden"> |
|
<t-menu :value="route.path" theme="dark" class="h-full bg-transparent "> |
|
<template #logo> |
|
<router-link to="/" class="flex items-center gap-2 p-4"> |
|
<img :src="logo" alt="logo" class="w-8 h-8" /> |
|
<h1 class="text-xl font-bold text-white">SEED LOG</h1> |
|
</router-link> |
|
</template> |
|
<t-menu-item v-for="item in menu" :key="item.path" :value="item.path" :to="item.path" |
|
@click="menuVisible = false" class="menu-item mx-4 my-2 rounded-xl"> |
|
<template #icon> |
|
<t-icon :name="item.icon" /> |
|
</template> |
|
{{ item.name }} |
|
</t-menu-item> |
|
</t-menu> |
|
</t-drawer> |
|
|
|
|
|
<t-aside class="sidebar backdrop-blur-lg hidden lg:block"> |
|
<t-menu :value="route.path" theme="dark" class="bg-transparent"> |
|
<template #logo> |
|
<router-link to="/" class="flex items-center gap-2"> |
|
<img :src="logo" alt="logo" class="w-10 h-10 transition-transform hover:scale-110" /> |
|
<h1 class="text-2xl font-bold text-white tracking-wider">SEED LOG</h1> |
|
</router-link> |
|
</template> |
|
<t-menu-item v-for="item in menu" :key="item.path" :value="item.path" :to="item.path" |
|
class="menu-item mx-4 my-2 rounded-xl"> |
|
<template #icon> |
|
<t-icon :name="item.icon" class="text-xl" /> |
|
</template> |
|
<span class="font-medium">{{ item.name }}</span> |
|
</t-menu-item> |
|
</t-menu> |
|
</t-aside> |
|
|
|
|
|
<t-layout class="w-full"> |
|
<t-header class="header backdrop-blur-xl border-b border-gray-100"> |
|
<div class="flex items-center justify-between h-full px-2 sm:px-4 lg:px-8"> |
|
|
|
<div class="flex items-center gap-2 sm:gap-4 "> |
|
<div class="lg:hidden"> |
|
<t-button theme="default" variant="text" class=" min-w-[40px]" @click="toggleMenu"> |
|
<t-icon name="menu" size="20px" sm:size="24px" /> |
|
</t-button> |
|
</div> |
|
<h1 |
|
class="text-base sm:text-xl lg:text-2xl font-bold bg-gradient-to-r from-blue-600 to-indigo-600 bg-clip-text text-transparent truncate max-w-[150px] sm:max-w-full"> |
|
{{ jwtToken?.sub }} |
|
</h1> |
|
</div> |
|
<div class="flex items-center gap-2 sm:gap-4"> |
|
<t-button theme="danger" @click="logout" class="logout-btn text-sm sm:text-base py-1 px-2 sm:px-4"> |
|
<template #icon> |
|
<t-icon name="logout" class="text-sm sm:text-base" /> |
|
</template> |
|
<span>退出</span> |
|
</t-button> |
|
</div> |
|
</div> |
|
</t-header> |
|
|
|
|
|
<t-content class="content bg-gray-50/30 flex-1 overflow-y-auto w-full"> |
|
<router-view v-slot="{ Component }"> |
|
<transition name="fade-slide" mode="out-in"> |
|
<keep-alive> |
|
<component :is="Component" /> |
|
</keep-alive> |
|
</transition> |
|
</router-view> |
|
</t-content> |
|
|
|
|
|
<t-footer class="footer backdrop-blur-sm py-4 text-center text-sm text-gray-600"> |
|
<span class="opacity-75">© 微软邮箱管理系统</span> |
|
</t-footer> |
|
</t-layout> |
|
|
|
</t-layout> |
|
</template> |
|
|
|
<style scoped> |
|
@reference "./assets/base.css"; |
|
|
|
.sidebar { |
|
@apply bg-gradient-to-br from-blue-600 via-blue-700 to-indigo-800; |
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); |
|
} |
|
|
|
.header { |
|
@apply bg-white/80 sticky top-0 z-10; |
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); |
|
} |
|
|
|
.menu-item { |
|
@apply transition-all duration-300 hover:bg-white/15 active:scale-95; |
|
@apply flex items-center gap-3 px-4 py-3; |
|
} |
|
|
|
.logout-btn { |
|
@apply transition-transform hover:scale-105 active:scale-95; |
|
} |
|
|
|
.fade-slide-enter-active, |
|
.fade-slide-leave-active { |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.fade-slide-enter-from { |
|
opacity: 0; |
|
transform: translateY(20px); |
|
} |
|
|
|
.fade-slide-leave-to { |
|
opacity: 0; |
|
transform: translateY(-20px); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
:deep(.t-drawer__body) { |
|
@apply p-0; |
|
} |
|
|
|
:deep(.t-drawer__body) { |
|
@apply p-0; |
|
} |
|
|
|
:deep(::-webkit-scrollbar) { |
|
@apply w-2; |
|
} |
|
|
|
:deep(::-webkit-scrollbar-thumb) { |
|
@apply bg-gray-400/30 rounded-full transition-colors hover:bg-gray-400/50; |
|
} |
|
|
|
:deep(::-webkit-scrollbar-track) { |
|
@apply bg-transparent; |
|
} |
|
|
|
.content { |
|
background-image: radial-gradient(circle at 50% 50%, |
|
rgba(255, 255, 255, 0.8) 0%, |
|
rgba(240, 240, 250, 0.6) 100%); |
|
} |
|
|
|
.footer { |
|
@apply bg-white/60; |
|
} |
|
|
|
|
|
@media (max-width: 1024px) { |
|
.sidebar { |
|
display: none; |
|
} |
|
} |
|
|
|
.drawer-menu { |
|
@apply bg-gradient-to-br from-blue-600 via-blue-700 to-indigo-800; |
|
} |
|
</style> |
|
|