初始化
This commit is contained in:
335
client/src/components/admin/AdminLayout.vue
Normal file
335
client/src/components/admin/AdminLayout.vue
Normal file
@@ -0,0 +1,335 @@
|
||||
<template>
|
||||
<div class="admin-layout">
|
||||
<!-- Sidebar Navigation -->
|
||||
<aside class="admin-sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h1 class="sidebar-title">管理后台</h1>
|
||||
</div>
|
||||
<nav class="sidebar-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link to="/admin/dashboard" class="nav-link" active-class="active">
|
||||
<span class="nav-icon">📊</span>
|
||||
<span class="nav-text">仪表盘</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/admin/users" class="nav-link" active-class="active">
|
||||
<span class="nav-icon">👥</span>
|
||||
<span class="nav-text">用户管理</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/admin/roles" class="nav-link" active-class="active">
|
||||
<span class="nav-icon">🔒</span>
|
||||
<span class="nav-text">角色管理</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/admin/posts" class="nav-link" active-class="active">
|
||||
<span class="nav-icon">📝</span>
|
||||
<span class="nav-text">文章管理</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/admin/works" class="nav-link" active-class="active">
|
||||
<span class="nav-icon">🎨</span>
|
||||
<span class="nav-text">作品管理</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/admin/snippets" class="nav-link" active-class="active">
|
||||
<span class="nav-icon">💻</span>
|
||||
<span class="nav-text">代码片段</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/admin/settings" class="nav-link" active-class="active">
|
||||
<span class="nav-icon">⚙️</span>
|
||||
<span class="nav-text">系统配置</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/admin/logs" class="nav-link" active-class="active">
|
||||
<span class="nav-icon">📋</span>
|
||||
<span class="nav-text">操作日志</span>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="sidebar-footer">
|
||||
<button @click="handleLogout" class="logout-btn">
|
||||
<span class="nav-icon">🚪</span>
|
||||
<span class="nav-text">退出登录</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main class="admin-main">
|
||||
<!-- Top Navigation Bar -->
|
||||
<header class="admin-header">
|
||||
<div class="header-left">
|
||||
<button class="toggle-btn" @click="toggleSidebar">
|
||||
☰
|
||||
</button>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="user-info">
|
||||
<span class="username">{{ currentUser.username }}</span>
|
||||
<span class="role">({{ currentUser.role }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Content Container -->
|
||||
<div class="admin-content">
|
||||
<router-view />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useToast } from '../../composables/useToast'
|
||||
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
const isSidebarOpen = ref(true)
|
||||
const currentUser = ref(JSON.parse(localStorage.getItem('user') || '{}'))
|
||||
|
||||
const toggleSidebar = () => {
|
||||
isSidebarOpen.value = !isSidebarOpen.value
|
||||
}
|
||||
|
||||
const handleLogout = () => {
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('user')
|
||||
toast.showToast('退出登录成功', 'success')
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
// Check if user is authenticated
|
||||
onMounted(() => {
|
||||
const token = localStorage.getItem('token')
|
||||
if (!token) {
|
||||
router.push('/login')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-layout {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
background-color: #050505;
|
||||
color: #ececec;
|
||||
}
|
||||
|
||||
/* Sidebar Styles */
|
||||
.admin-sidebar {
|
||||
width: 250px;
|
||||
background: rgba(5, 5, 5, 0.7);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.03);
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
color: #d4b383;
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
flex: 1;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.sidebar-nav ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1.5rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
text-decoration: none;
|
||||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
border-left: 3px solid transparent;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background-color: rgba(212, 179, 131, 0.1);
|
||||
color: white;
|
||||
border-left-color: rgba(212, 179, 131, 0.3);
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
background-color: rgba(212, 179, 131, 0.15);
|
||||
color: #d4b383;
|
||||
border-left-color: #d4b383;
|
||||
box-shadow: 0 0 15px rgba(212, 179, 131, 0.1);
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
margin-right: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
font-size: 0.95rem;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
padding: 1rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background-color: transparent;
|
||||
color: #d4b383;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
font-size: 0.95rem;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.logout-btn:hover {
|
||||
background-color: rgba(212, 179, 131, 0.1);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(212, 179, 131, 0.1);
|
||||
}
|
||||
|
||||
/* Main Content Styles */
|
||||
.admin-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Header Styles */
|
||||
.admin-header {
|
||||
background: rgba(5, 5, 5, 0.7);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
|
||||
padding: 0 1.5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.toggle-btn:hover {
|
||||
background-color: rgba(212, 179, 131, 0.1);
|
||||
color: #d4b383;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.95rem;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: 600;
|
||||
color: #d4b383;
|
||||
}
|
||||
|
||||
.role {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Content Area */
|
||||
.admin-content {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Scrollbar Styles */
|
||||
.admin-content::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.admin-content::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.admin-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 3px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.admin-content::-webkit-scrollbar-thumb:hover {
|
||||
background: #d4b383;
|
||||
box-shadow: 0 0 10px rgba(212, 179, 131, 0.4);
|
||||
}
|
||||
|
||||
.admin-content::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.admin-sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
z-index: 1000;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.admin-sidebar.collapsed {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user