feat: 完成 #2 布局组件开发与移动端适配
This commit is contained in:
@@ -1,7 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-container class="layout-container">
|
<el-container class="layout-container">
|
||||||
|
<div
|
||||||
|
v-if="isMobile && mobileMenuVisible"
|
||||||
|
class="mobile-mask"
|
||||||
|
@click="closeMobileMenu"
|
||||||
|
></div>
|
||||||
|
|
||||||
<!-- 侧边栏 -->
|
<!-- 侧边栏 -->
|
||||||
<el-aside :width="isCollapse ? '64px' : '200px'" class="sidebar">
|
<el-aside :width="asideWidth" class="sidebar" :class="{ 'sidebar-mobile': isMobile }">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<span v-if="!isCollapse">管理后台</span>
|
<span v-if="!isCollapse">管理后台</span>
|
||||||
<span v-else>后台</span>
|
<span v-else>后台</span>
|
||||||
@@ -9,9 +15,10 @@
|
|||||||
|
|
||||||
<el-menu
|
<el-menu
|
||||||
:default-active="activeMenu"
|
:default-active="activeMenu"
|
||||||
:collapse="isCollapse"
|
:collapse="menuCollapse"
|
||||||
:unique-opened="true"
|
:unique-opened="true"
|
||||||
router
|
router
|
||||||
|
@select="handleMenuSelect"
|
||||||
>
|
>
|
||||||
<el-menu-item
|
<el-menu-item
|
||||||
v-for="route in menuRoutes"
|
v-for="route in menuRoutes"
|
||||||
@@ -61,7 +68,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||||
import { Fold, Expand, User, ArrowDown } from '@element-plus/icons-vue'
|
import { Fold, Expand, User, ArrowDown } from '@element-plus/icons-vue'
|
||||||
@@ -72,8 +79,27 @@ const route = useRoute()
|
|||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const isCollapse = ref(false)
|
const isCollapse = ref(false)
|
||||||
|
const isMobile = ref(window.innerWidth < 768)
|
||||||
|
const mobileMenuVisible = ref(false)
|
||||||
|
|
||||||
|
const asideWidth = computed(() => {
|
||||||
|
if (isMobile.value) {
|
||||||
|
return mobileMenuVisible.value ? '200px' : '0px'
|
||||||
|
}
|
||||||
|
return isCollapse.value ? '64px' : '200px'
|
||||||
|
})
|
||||||
|
|
||||||
|
const menuCollapse = computed(() => {
|
||||||
|
if (isMobile.value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isCollapse.value
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
window.addEventListener('resize', handleResize)
|
||||||
|
handleResize()
|
||||||
|
|
||||||
if (userStore.token && !userStore.userInfo) {
|
if (userStore.token && !userStore.userInfo) {
|
||||||
try {
|
try {
|
||||||
await userStore.fetchUserInfo()
|
await userStore.fetchUserInfo()
|
||||||
@@ -83,6 +109,19 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('resize', handleResize)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.path,
|
||||||
|
() => {
|
||||||
|
if (isMobile.value) {
|
||||||
|
closeMobileMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 获取菜单路由(过滤掉隐藏的路由)
|
// 获取菜单路由(过滤掉隐藏的路由)
|
||||||
const menuRoutes = computed(() => {
|
const menuRoutes = computed(() => {
|
||||||
const routes = router.getRoutes()
|
const routes = router.getRoutes()
|
||||||
@@ -104,9 +143,30 @@ const activeMenu = computed(() => {
|
|||||||
|
|
||||||
// 切换侧边栏折叠状态
|
// 切换侧边栏折叠状态
|
||||||
const toggleCollapse = () => {
|
const toggleCollapse = () => {
|
||||||
|
if (isMobile.value) {
|
||||||
|
mobileMenuVisible.value = !mobileMenuVisible.value
|
||||||
|
return
|
||||||
|
}
|
||||||
isCollapse.value = !isCollapse.value
|
isCollapse.value = !isCollapse.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const closeMobileMenu = () => {
|
||||||
|
mobileMenuVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMenuSelect = () => {
|
||||||
|
if (isMobile.value) {
|
||||||
|
closeMobileMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResize = () => {
|
||||||
|
isMobile.value = window.innerWidth < 768
|
||||||
|
if (!isMobile.value) {
|
||||||
|
mobileMenuVisible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 处理下拉菜单命令
|
// 处理下拉菜单命令
|
||||||
const handleCommand = async (command) => {
|
const handleCommand = async (command) => {
|
||||||
if (command === 'logout') {
|
if (command === 'logout') {
|
||||||
@@ -133,11 +193,14 @@ const handleCommand = async (command) => {
|
|||||||
.layout-container {
|
.layout-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
background: #304156;
|
background: #304156;
|
||||||
transition: width 0.3s;
|
transition: width 0.3s;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1001;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
@@ -218,4 +281,28 @@ const handleCommand = async (command) => {
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile-mask {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.sidebar-mobile {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -61,6 +61,12 @@ const routes = [
|
|||||||
component: () => import('../views/expiry/index.vue'),
|
component: () => import('../views/expiry/index.vue'),
|
||||||
meta: { title: '保质期管理', icon: 'Box' }
|
meta: { title: '保质期管理', icon: 'Box' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'watermark',
|
||||||
|
name: 'Watermark',
|
||||||
|
component: () => import('../views/watermark/index.vue'),
|
||||||
|
meta: { title: '去水印管理', icon: 'Brush' }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'settings',
|
path: 'settings',
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<template>
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<span>去水印管理</span>
|
||||||
|
</template>
|
||||||
|
<el-empty description="开发中,下一步对接去水印任务管理接口" />
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user