Files
mversion-ui/src/pages/Layout/index.vue
2026-01-05 19:28:55 +08:00

286 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="mj-layout">
<el-container>
<el-aside :width="width">
<div class="mj-aside-content">
<!-- 顶部company公司标记 -->
<div class="mj-aside-logo">
<img :src="companyLogo" class="mj-company-logo" />
</div>
<div class="mj-aside-menu">
<div class="mj-aside-title" v-show="!isCollapse">
{{ topTitle }}
</div>
<standardMenu
class="mj-aside_menu"
:isCollapse="isCollapse"
:menuList="sideMenuList"
:active-menu="selectedActiveMenu"
@menu-select="handleSideMenuSelect"
/>
</div>
<!-- 展开收缩左侧菜单按钮 -->
<div class="mj-collapse" @click="showCollapse">
<el-icon><component :is="isCollapse ? 'DArrowRight' : 'DArrowLeft'" /></el-icon>
</div>
</div>
</el-aside>
<el-container>
<el-header class="mj-header-content">
<!-- 左侧的菜单展示 -->
<standardMenu
:menuList="topLevelMenuList"
mode="horizontal"
:active-menu="selectedTopMenu"
@menu-select="handleTopMenuSelect"
/>
<!-- 右侧用户的内容 -->
<rightMenuGroup @on-stage-manage="onStageManage" />
</el-header>
<el-main>
<!-- <card-item :list="[1,2,3,4,5,6]"/> -->
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script setup lang="ts">
import standardMenu from "@/components/standardMenu/index.vue";
import { useUserStore } from "@/store";
import rightMenuGroup from './rightMenuGroup.vue';
import companyLogo from '@/assets/images/logo.png';
defineOptions({
name: "Layout",
});
const userStore = useUserStore();
const router = useRouter();
// 响应式断点(小屏阈值,小于此值视为小屏)
const BREAKPOINT = 1024;
// 屏幕宽度
const screenWidth = ref(window.innerWidth);
// 菜单收缩状态:完全根据屏幕大小自动判断
const isCollapse = computed(() => {
return screenWidth.value < BREAKPOINT;
});
const width = computed(() => {
return isCollapse.value ? "80px" : "224px";
});
// 监听窗口大小变化
const handleResize = () => {
screenWidth.value = window.innerWidth;
// isCollapse 是计算属性,会自动响应 screenWidth 的变化
};
// 展开收缩菜单(临时切换,窗口大小变化时会自动恢复)
const showCollapse = () => {
if (isCollapse.value) {
screenWidth.value = BREAKPOINT + 1;
} else {
screenWidth.value = BREAKPOINT - 1;
}
};
// 返回菜单数据
const menuList = computed(() => {
return userStore.routes || [];
});
// 获取一级菜单数据(不包含 children
const topLevelMenuList = computed(() => {
return menuList.value.map((item) => {
const { children, ...rest } = item;
return rest;
}).filter(itv=>itv.name !== 'stage');
});
// 获取是管理后台中心的数据内容
const backTitle = computed(()=>{
return menuList.value.find(itv=>itv.name === 'stage')?.meta?.title || '-';
})
// 后台管理点击获取列表
const onStageManage = () =>{
selectedTopMenu.value = '/stage';
}
const topTitle = computed(() => {
return (
topLevelMenuList.value.find((path) => path.path === selectedTopMenu.value)
?.meta?.title || backTitle.value
);
});
// 当前选中的顶部菜单
const selectedTopMenu = ref<string>("");
const selectedActiveMenu = ref<string>("");
// 根据选中的顶部菜单,获取左侧菜单列表
const sideMenuList = computed(() => {
if (!selectedTopMenu.value) {
// 默认选中第一个有 children 的菜单
const firstMenuWithChildren = menuList.value.find(
(item) => item.children && item.children.length > 0
);
if (firstMenuWithChildren) {
return (firstMenuWithChildren.children || []).map((child) => {
const fullPath = child.path.startsWith("/")
? child.path
: `${firstMenuWithChildren.path}/${child.path}`;
return {
...child,
path: fullPath,
};
});
}
return [];
}
// 根据选中的顶部菜单路径,找到对应的菜单项
const selectedMenu = menuList.value.find(
(item) => item.path === selectedTopMenu.value
);
if (selectedMenu && selectedMenu.children) {
return selectedMenu.children.map((child) => {
const fullPath = child.path.startsWith("/")
? child.path
: `${selectedMenu.path}/${child.path}`;
return {
...child,
path: fullPath,
};
});
}
return [];
});
// 处理顶部菜单选中事件
const handleTopMenuSelect = (menuPath: string) => {
selectedTopMenu.value = menuPath;
};
// 左侧菜单选中事件
const handleSideMenuSelect = (menuPath: string) => {
selectedActiveMenu.value = menuPath;
};
// 初始化:默认选中第一个菜单
onMounted(() => {
const currentRoutePath = router.currentRoute.value.path;
const matchedTopMenu = topLevelMenuList.value.find(menu =>
currentRoutePath.startsWith(`${menu.path}/`) || currentRoutePath === menu.path
);
if (matchedTopMenu && matchedTopMenu.path) {
selectedTopMenu.value = matchedTopMenu.path;
} else if (topLevelMenuList.value.length > 0) {
// 否则默认选中第一个菜单
const firstMenu = topLevelMenuList.value[0];
if (firstMenu && firstMenu.path) {
selectedTopMenu.value = firstMenu.path;
}
}
// 监听窗口大小变化
window.addEventListener("resize", handleResize);
// 初始化时根据屏幕大小设置菜单状态
handleResize();
});
// 组件卸载时移除监听
onUnmounted(() => {
window.removeEventListener("resize", handleResize);
});
</script>
<style lang="scss" scoped>
.mj-layout {
height: inherit;
:deep(.el-container) {
height: inherit;
}
:deep(.el-aside) {
transition: width 0.3s;
overflow: hidden;
}
:deep(.el-main) {
--el-main-padding: 16px;
background-color: #f8fafc;
}
.mj-aside-content {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
border-right: 1px solid var(--mj-border-color);
.mj-aside-logo {
height: var(--mj-menu-header-height);
line-height: var(--mj-menu-header-height);
border-bottom: 1px solid var(--mj-border-color);
flex-shrink: 0;
.mj-company-logo{
width: 39px;
height: 32px;
display: inline-block;
vertical-align: middle;
margin-left: 20px;
}
}
.mj-aside-menu {
flex: 1;
overflow: hidden;
.mj-aside-title {
font-size: 10px;
color: #888;
padding: 10px var(--el-menu-base-level-padding);
transition: opacity 0.3s;
}
}
.mj-collapse {
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
border-radius: 50%;
position: absolute;
right: 0;
top: calc(var(--mj-menu-header-height) / 2 - 10px);
cursor: pointer;
z-index: 10;
box-sizing: border-box;
box-shadow: 0 1px 6px #0000001f;
}
}
.mj-header-content{
display: flex;
justify-content: space-between;
.mj-standard-menu{
flex: 1;
}
}
.mj-header-content {
--el-header-padding: 0;
border-bottom: 1px solid var(--mj-border-color);
}
}
</style>