添加 vue-router 依赖并配置路由,重构登录组件,完善 HTTP 请求拦截器以支持 token 刷新机制

This commit is contained in:
zyh
2025-08-24 19:49:39 +08:00
parent 69bf5500cd
commit 17a1d4e85a
30 changed files with 2368 additions and 28 deletions

138
src/layouts/AdminLayout.vue Normal file
View File

@@ -0,0 +1,138 @@
<template>
<div class="admin-layout">
<aside class="sider">
<div class="brand">
<img class="logo" src="https://vuejs.org/images/logo.png" alt="logo" />
<span class="name">管理后台</span>
</div>
<el-menu
class="menu"
router
:default-active="$route.name"
:collapse="collapsed"
background-color="#001529"
text-color="#bfcbd9"
active-text-color="#fff"
>
<el-menu-item index="Dashboard" :route="{ name: 'Dashboard' }">
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/></svg></i>
<span>仪表盘</span>
</el-menu-item>
<el-menu-item v-if="canAccessUsers" index="Users" :route="{ name: 'Users' }">
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 12c2.7 0 5-2.3 5-5s-2.3-5-5-5s-5 2.3-5 5s2.3 5 5 5m0 2c-3.3 0-10 1.7-10 5v3h20v-3c0-3.3-6.7-5-10-5Z"/></svg></i>
<span>用户管理</span>
</el-menu-item>
<el-menu-item v-if="canAccessGames" index="Games" :route="{ name: 'Games' }">
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M6 8h12v8H6z"/></svg></i>
<span>游戏管理</span>
</el-menu-item>
<el-menu-item v-if="canAccessOrders" index="Orders" :route="{ name: 'Orders' }">
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg></i>
<span>订单管理</span>
</el-menu-item>
<el-menu-item v-if="canAccessReports" index="Reports" :route="{ name: 'Reports' }">
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M3 3h2v18H3V3m4 8h2v10H7V11m4-6h2v16h-2V5m4 10h2v6h-2v-6m4-3h2v9h-2v-9Z"/></svg></i>
<span>报表分析</span>
</el-menu-item>
<el-menu-item v-if="canAccessSettings" index="Settings" :route="{ name: 'Settings' }">
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="m12 8l-2 4h4l-2 4"/></svg></i>
<span>系统设置</span>
</el-menu-item>
<el-menu-item index="ErrorTest" :route="{ name: 'ErrorTest' }">
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg></i>
<span>错误测试</span>
</el-menu-item>
<el-menu-item index="PermissionTest" :route="{ name: 'PermissionTest' }">
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg></i>
<span>权限测试</span>
</el-menu-item>
</el-menu>
</aside>
<section class="main">
<header class="header">
<el-button text @click="collapsed = !collapsed" class="collapse-btn">
<svg width="20" height="20" viewBox="0 0 24 24"><path fill="currentColor" d="M3 6h18v2H3V6m0 5h12v2H3v-2m0 5h18v2H3v-2Z"/></svg>
</el-button>
<div class="spacer" />
<el-dropdown>
<span class="el-dropdown-link">
管理员<i class="el-icon el-icon--right"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentColor" d="M7 10l5 5 5-5z"/></svg></i>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="onProfile">个人中心</el-dropdown-item>
<el-dropdown-item divided @click="onLogout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</header>
<main class="content">
<router-view />
</main>
</section>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { clearTokens } from '@/utils/auth'
import { useRouter } from 'vue-router'
import { canAccessRoute } from '@/utils/permission'
const collapsed = ref(false)
const router = useRouter()
// 权限检查
const canAccessUsers = computed(() => canAccessRoute('Users'))
const canAccessGames = computed(() => canAccessRoute('Games'))
const canAccessOrders = computed(() => canAccessRoute('Orders'))
const canAccessReports = computed(() => canAccessRoute('Reports'))
const canAccessSettings = computed(() => canAccessRoute('Settings'))
function onProfile() {
// 可跳转到个人中心占位页
}
function onLogout() {
clearTokens()
router.replace({ name: 'Login' })
}
</script>
<style scoped>
.admin-layout {
display: flex;
height: 100vh;
}
.sider {
width: 220px;
background: #001529;
color: #bfcbd9;
display: flex;
flex-direction: column;
}
.brand {
height: 56px;
display: flex;
align-items: center;
gap: 10px;
padding: 0 16px;
color: #fff;
}
.logo { width: 24px; height: 24px; }
.menu { border-right: none; flex: 1; }
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.header {
height: 56px;
display: flex;
align-items: center;
padding: 0 16px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
}
.collapse-btn { margin-right: 8px; }
.spacer { flex: 1; }
.content { padding: 16px; overflow: auto; background: #f5f7fa; height: calc(100vh - 56px); }
</style>