【改进】主页动态化

This commit is contained in:
2025-10-31 17:39:24 +08:00
parent ad98033faa
commit 74034ed26a
2 changed files with 211 additions and 281 deletions

View File

@@ -141,96 +141,9 @@ body {
/* 内容区域 */ /* 内容区域 */
.content-wrapper { .content-wrapper {
padding: 25px; padding: 0;
} }
/* 欢迎卡片 */
.welcome-card {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
color: white;
border-radius: 16px;
padding: 35px;
margin-bottom: 25px;
box-shadow: 0 10px 30px rgba(91, 95, 222, 0.2);
position: relative;
overflow: hidden;
}
.welcome-card::before {
content: '';
position: absolute;
width: 250px;
height: 250px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
top: -80px;
right: -80px;
}
.welcome-card h2 {
font-size: 26px;
font-weight: 700;
margin-bottom: 10px;
position: relative;
z-index: 1;
}
.welcome-card p {
font-size: 15px;
opacity: 0.95;
position: relative;
z-index: 1;
}
/* 统计卡片 */
.stat-card {
background: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
transition: all 0.3s;
border: 1px solid #E5E7EB;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.stat-card i {
font-size: 40px;
opacity: 0.9;
margin-bottom: 12px;
}
.stat-card h3 {
font-size: 30px;
font-weight: 700;
margin: 12px 0 6px;
color: #1F2937;
}
.stat-card p {
color: #6B7280;
font-size: 13px;
margin: 0;
}
.stat-card.primary i {
color: var(--primary-color);
}
.stat-card.success i {
color: var(--success-color);
}
.stat-card.warning i {
color: var(--warning-color);
}
.stat-card.info i {
color: var(--info-color);
}
/* 页面标题 */ /* 页面标题 */
.page-title { .page-title {
@@ -252,6 +165,24 @@ body {
padding: 25px; padding: 25px;
} }
/* iframe样式 */
.page-iframe {
width: 100%;
height: calc(100vh - 72px); /* 减去导航栏高度 */
border: none;
transition: opacity 0.3s ease;
border-radius: 8px;
}
/* 加载状态 */
.loading-state {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
/* 响应式 */ /* 响应式 */
@media (max-width: 768px) { @media (max-width: 768px) {
.sidebar { .sidebar {
@@ -278,6 +209,9 @@ body {
} }
.content-wrapper { .content-wrapper {
padding: 20px; padding: 0;
min-height: calc(100vh - 72px);
} }
} }

View File

@@ -14,7 +14,7 @@
<link rel="stylesheet" href="/assets/css/main.css"> <link rel="stylesheet" href="/assets/css/main.css">
</head> </head>
<body> <body>
<div id="app"> <div id="app">
<!-- 侧边栏 --> <!-- 侧边栏 -->
<nav class="sidebar"> <nav class="sidebar">
<div class="sidebar-header"> <div class="sidebar-header">
@@ -27,34 +27,26 @@
<div class="sidebar-menu"> <div class="sidebar-menu">
<ul class="nav flex-column"> <ul class="nav flex-column">
<li class="nav-item"> <li class="nav-item">
<a :class="['nav-link', currentPage === 'dashboard' ? 'active' : '']" <a :class="['nav-link', currentUrl === '/admin/dashboard' ? 'active' : '']"
href="#" href="#"
@click.prevent="currentPage = 'dashboard'"> @click.prevent="changeMenu('/admin/dashboard')">
<i class="bi bi-speedometer2"></i> <i class="bi bi-speedometer2"></i>
仪表盘 仪表盘
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item" v-for="(item, index) in menus" :key="index">
<a :class="['nav-link', currentPage === 'users' ? 'active' : '']" <a :class="['nav-link', currentUrl === item.menuUrl ? 'active' : '']"
href="#" href="#"
@click.prevent="currentPage = 'users'"> @click.prevent="changeMenu(item.menuUrl)">
<i class="bi bi-people-fill"></i> <i :class="item.menuIcon"></i>
用户管理 {{ item.menuName }}
</a>
</li>
<li class="nav-item">
<a :class="['nav-link', currentPage === 'settings' ? 'active' : '']"
href="#"
@click.prevent="currentPage = 'settings'">
<i class="bi bi-gear-fill"></i>
系统设置
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="sidebar-footer"> <div class="sidebar-footer">
<button class="btn-logout" @click="handleLogout"> <button class="btn-logout" data-bs-toggle="modal" data-bs-target="#exitModal">
<i class="bi bi-box-arrow-right me-2"></i> <i class="bi bi-box-arrow-right me-2"></i>
退出登录 退出登录
</button> </button>
@@ -67,7 +59,7 @@
<nav class="navbar navbar-expand-lg navbar-light"> <nav class="navbar navbar-expand-lg navbar-light">
<div class="container-fluid"> <div class="container-fluid">
<span class="navbar-brand">欢迎回来</span> <span class="navbar-brand">欢迎回来</span>
<div class="user-info"> <div class="user-info" style="cursor: pointer">
<i class="bi bi-person-circle"></i> <i class="bi bi-person-circle"></i>
<span>{{ userInfo.realName || userInfo.username }}</span> <span>{{ userInfo.realName || userInfo.username }}</span>
</div> </div>
@@ -76,79 +68,52 @@
<!-- 内容区域 --> <!-- 内容区域 -->
<div class="content-wrapper"> <div class="content-wrapper">
<!-- 仪表盘 --> <!-- iframe容器 - 用于加载菜单对应的URL页面 -->
<div v-if="currentPage === 'dashboard'"> <iframe
<div class="welcome-card"> v-if="currentUrl"
<h2> class="page-iframe"
<i class="bi bi-emoji-smile"></i> :src="currentUrl"
你好,{{ userInfo.realName || userInfo.username }} @load="handleIframeLoad"
</h2> title="页面内容"
<p class="mb-0 mt-2">欢迎使用 CoreWing 后台管理系统</p> ></iframe>
</div>
<div class="row">
<div class="col-md-4 mb-4">
<div class="stat-card primary">
<i class="bi bi-people-fill"></i>
<h3>1,234</h3>
<p>总用户数</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="stat-card success">
<i class="bi bi-file-earmark-text-fill"></i>
<h3>567</h3>
<p>文档数量</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="stat-card warning">
<i class="bi bi-graph-up"></i>
<h3>89%</h3>
<p>系统负载</p>
</div>
</div>
</div>
</div>
<!-- 用户管理 -->
<div v-if="currentPage === 'users'">
<h3 class="page-title">用户管理</h3>
<div class="card">
<div class="card-body">
<p>用户管理功能开发中...</p>
</div>
</div>
</div>
<!-- 系统设置 -->
<div v-if="currentPage === 'settings'">
<h3 class="page-title">系统设置</h3>
<div class="card">
<div class="card-body">
<p>系统设置功能开发中...</p>
</div>
</div>
</div>
</div> </div>
</main> </main>
<div class="modal fade" id="exitModal" tabindex="-1" aria-labelledby="exitModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">系统提示</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body">
您确认退出系统吗?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" @click="handleLogout">退出</button>
</div>
</div>
</div>
</div>
</div>
<!-- Vue 3 --> <!-- Vue 3 -->
<script src="https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js"></script>
<!-- Axios --> <!-- Axios -->
<script src="https://cdn.jsdelivr.net/npm/axios@1.4.0/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios@1.4.0/dist/axios.min.js"></script>
<!-- Bootstrap 5 JS --> <!-- Bootstrap 5 JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<!-- 引入封装的 axiosRequest.js --> <!-- 引入封装的 axiosRequest.js -->
<script src="/assets/js/axiosRequest.js"></script> <script src="/assets/js/axiosRequest.js"></script>
<script> <script>
const { createApp } = Vue; const {createApp} = Vue;
createApp({ createApp({
data() { data() {
@@ -158,7 +123,9 @@
realName: '', realName: '',
userId: '' userId: ''
}, },
currentPage: 'dashboard' menus: [],
currentUrl: '/admin/dashboard', // 当前加载的URL
isLoading: false // 加载状态
} }
}, },
methods: { methods: {
@@ -167,11 +134,12 @@
this.userInfo.username = localStorage.getItem('username') || ''; this.userInfo.username = localStorage.getItem('username') || '';
this.userInfo.realName = localStorage.getItem('realName') || ''; this.userInfo.realName = localStorage.getItem('realName') || '';
this.userInfo.userId = localStorage.getItem('userId') || ''; this.userInfo.userId = localStorage.getItem('userId') || '';
const token = localStorage.getItem("token");
if (token) { if (token) {
try { try {
const response = await request.get('/sys/user/info'); const response = await request.get('/sys/user/info');
if (response.data.code === 200) { if (response.code === 200) {
const user = response.data.data; const user = response.data;
this.userInfo.username = user.username; this.userInfo.username = user.username;
this.userInfo.realName = user.realName || user.username; this.userInfo.realName = user.realName || user.username;
this.userInfo.userId = user.id; this.userInfo.userId = user.id;
@@ -183,7 +151,6 @@
}, },
async handleLogout() { async handleLogout() {
if (confirm('确定要退出登录吗?')) {
try { try {
await request.get('/sys/user/logout'); await request.get('/sys/user/logout');
} catch (error) { } catch (error) {
@@ -194,7 +161,6 @@
// 跳转到登录页 // 跳转到登录页
window.location.href = '/admin/login.html'; window.location.href = '/admin/login.html';
} }
}
}, },
checkLogin() { checkLogin() {
@@ -207,9 +173,34 @@
async initMenu() { async initMenu() {
const response = await request.get('/sys/menu/initSysMenu'); const response = await request.get('/sys/menu/initSysMenu');
if(response.data.code === 200) { if (response.code === 200) {
console.log(response.data.code); this.menus = response.data;
} }
this.initCurrentUrlFromHash();
},
// 从浏览器hash初始化当前URL
initCurrentUrlFromHash() {
const hash = window.location.hash.slice(1); // 获取 # 后的内容(如 #/admin/user 取 /admin/user
// 如果hash存在且是有效菜单URL用hash否则用默认的仪表盘
const validMenuUrls = this.menus.map(item => item.menuUrl);
if (hash && (validMenuUrls.includes(hash) || hash === '/admin/dashboard')) {
this.currentUrl = hash;
} else {
this.currentUrl = '/admin/dashboard';
// 同步默认值到地址栏
window.location.hash = this.currentUrl;
}
},
// 切换菜单时更新URL和状态
changeMenu(menuUrl) {
this.currentUrl = menuUrl;
this.isLoading = true;
window.location.hash = menuUrl;
},
handleIframeLoad() {
console.log('加载完成');
this.isLoading = false;
} }
}, },
mounted() { mounted() {
@@ -219,8 +210,13 @@
this.initMenu(); this.initMenu();
// 加载用户信息 // 加载用户信息
this.loadUserInfo(); this.loadUserInfo();
this.isLoading = true;
// 监听浏览器hash变化刷新/前进后退时触发)
window.addEventListener('hashchange', () => {
this.initCurrentUrlFromHash();
});
} }
}).mount('#app'); }).mount('#app');
</script> </script>
</body> </body>
</html> </html>