优化移动端界面,新增退单管理功能,添加相关API接口,更新权限设置,调整布局以支持响应式设计,提升用户体验。
This commit is contained in:
@@ -1,18 +1,26 @@
|
||||
<template>
|
||||
<div class="admin-layout">
|
||||
<aside class="sider">
|
||||
<!-- 移动端遮罩层 -->
|
||||
<div
|
||||
v-if="isMobile && !collapsed"
|
||||
class="mobile-overlay"
|
||||
@click="collapsed = true"
|
||||
></div>
|
||||
|
||||
<aside :class="['sider', { 'sider-mobile': isMobile, 'sider-collapsed': isMobile && collapsed }]">
|
||||
<div class="brand">
|
||||
<img class="logo" src="https://vuejs.org/images/logo.png" alt="logo" />
|
||||
<span class="name">管理后台</span>
|
||||
<span v-show="!collapsed || !isMobile" class="name">管理后台</span>
|
||||
</div>
|
||||
<el-menu
|
||||
class="menu"
|
||||
router
|
||||
:default-active="$route.name"
|
||||
:collapse="collapsed"
|
||||
:collapse="collapsed && !isMobile"
|
||||
background-color="#001529"
|
||||
text-color="#bfcbd9"
|
||||
active-text-color="#fff"
|
||||
@select="onMenuSelect"
|
||||
>
|
||||
|
||||
<el-menu-item v-if="canAccessUsers" index="Users" :route="{ name: 'Users' }">
|
||||
@@ -26,6 +34,11 @@
|
||||
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.982 2.982 0 0 0 0-4.24a2.982 2.982 0 0 0-4.24 0l-3.53 3.53a2.982 2.982 0 0 0 0-4.24zm2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.982 2.982 0 0 0 0 4.24a2.982 2.982 0 0 0 4.24 0l3.53-3.53a2.982 2.982 0 0 0 0-4.24a.973.973 0 0 1 0-1.42z"/></svg></i>
|
||||
<span>链接管理</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item v-if="canAccessRefund" index="Refund" :route="{ name: 'Refund' }">
|
||||
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"/></svg></i>
|
||||
<span>退单管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item v-if="canAccessAnnouncements" index="Announcements" :route="{ name: 'Announcements' }">
|
||||
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg></i>
|
||||
<span>公告管理</span>
|
||||
@@ -40,13 +53,17 @@
|
||||
</aside>
|
||||
<section class="main">
|
||||
<header class="header">
|
||||
<el-button text @click="collapsed = !collapsed" class="collapse-btn">
|
||||
<el-button text @click="toggleSidebar" 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" />
|
||||
<!-- 移动端显示当前页面标题 -->
|
||||
<span v-if="isMobile" class="mobile-page-title">{{ currentPageTitle }}</span>
|
||||
<div v-if="!isMobile" class="spacer" />
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
{{ currentUser?.username || '用户' }}<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 v-if="!isMobile">{{ currentUser?.username || '用户' }}</span>
|
||||
<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>
|
||||
@@ -65,23 +82,61 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { clearTokens } from '@/utils/auth'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { canAccessRoute, getCurrentUser } from '@/utils/permission'
|
||||
|
||||
const collapsed = ref(false)
|
||||
const isMobile = ref(false)
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
// 检测移动端
|
||||
const checkMobile = () => {
|
||||
isMobile.value = window.innerWidth <= 768
|
||||
// 移动端默认收起侧边栏
|
||||
if (isMobile.value) {
|
||||
collapsed.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// 页面标题映射
|
||||
const pageTitleMap = {
|
||||
'Users': '用户管理',
|
||||
'Links': '链接管理',
|
||||
'Refund': '退单管理',
|
||||
'Announcements': '公告管理',
|
||||
'Settings': '系统设置'
|
||||
}
|
||||
|
||||
// 获取当前用户信息
|
||||
const currentUser = computed(() => getCurrentUser())
|
||||
|
||||
// 获取当前页面标题
|
||||
const currentPageTitle = computed(() => {
|
||||
return pageTitleMap[route.name] || '管理后台'
|
||||
})
|
||||
|
||||
// 权限检查
|
||||
const canAccessUsers = computed(() => canAccessRoute('Users'))
|
||||
const canAccessLinks = computed(() => canAccessRoute('Links'))
|
||||
const canAccessRefund = computed(() => canAccessRoute('Refund'))
|
||||
const canAccessAnnouncements = computed(() => canAccessRoute('Announcements'))
|
||||
const canAccessSettings = computed(() => canAccessRoute('Settings'))
|
||||
|
||||
// 切换侧边栏
|
||||
const toggleSidebar = () => {
|
||||
collapsed.value = !collapsed.value
|
||||
}
|
||||
|
||||
// 菜单选择事件(移动端选择后自动收起)
|
||||
const onMenuSelect = () => {
|
||||
if (isMobile.value) {
|
||||
collapsed.value = true
|
||||
}
|
||||
}
|
||||
|
||||
function onProfile() {
|
||||
// 可跳转到个人中心占位页
|
||||
}
|
||||
@@ -90,20 +145,61 @@ function onLogout() {
|
||||
clearTokens()
|
||||
router.replace({ name: 'Login' })
|
||||
}
|
||||
|
||||
// 监听窗口大小变化
|
||||
onMounted(() => {
|
||||
checkMobile()
|
||||
window.addEventListener('resize', checkMobile)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', checkMobile)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-layout {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 移动端遮罩层 */
|
||||
.mobile-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sider {
|
||||
width: 220px;
|
||||
background: #001529;
|
||||
color: #bfcbd9;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* 移动端侧边栏样式 */
|
||||
.sider-mobile {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
transform: translateX(0);
|
||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.sider-mobile.sider-collapsed {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.brand {
|
||||
height: 56px;
|
||||
display: flex;
|
||||
@@ -111,10 +207,34 @@ function onLogout() {
|
||||
gap: 10px;
|
||||
padding: 0 16px;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #334050;
|
||||
}
|
||||
.logo { width: 24px; height: 24px; }
|
||||
.menu { border-right: none; flex: 1; }
|
||||
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
|
||||
|
||||
.logo {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.menu {
|
||||
border-right: none;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 56px;
|
||||
display: flex;
|
||||
@@ -122,9 +242,110 @@ function onLogout() {
|
||||
padding: 0 16px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
margin-right: 8px;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.collapse-btn:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mobile-page-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.el-dropdown-link:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
background: #f5f7fa;
|
||||
height: calc(100vh - 56px);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 移动端响应式布局 */
|
||||
@media (max-width: 768px) {
|
||||
.main {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.mobile-page-title {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.el-dropdown-link {
|
||||
font-size: 16px;
|
||||
min-height: var(--mobile-touch-target);
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.header {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 桌面端样式 */
|
||||
@media (min-width: 769px) {
|
||||
.sider-mobile {
|
||||
position: relative;
|
||||
transform: none !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.mobile-overlay {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.collapse-btn { margin-right: 8px; }
|
||||
.spacer { flex: 1; }
|
||||
.content { padding: 16px; overflow: auto; background: #f5f7fa; height: calc(100vh - 56px); }
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user