完善页面布局
This commit is contained in:
@@ -22,14 +22,15 @@
|
||||
<!-- 二级侧边栏 (1:1 复刻 CRMEB 抽屉/Dock 平滑切换) -->
|
||||
<AdminSubSider
|
||||
:visible="isSubSiderVisible"
|
||||
:class="{ 'sub-sider-overlay': isSubSiderOverlay || layoutMode === 'mobile' }"
|
||||
:topMenuTitle="activeTopMenuTitle"
|
||||
:groups="activeGroups"
|
||||
:routes="activeRoutes"
|
||||
:activeRouteId="activeRouteId"
|
||||
:isOverlay="isOverlayVisible || layoutMode === 'mobile'"
|
||||
:layoutMode="layoutMode"
|
||||
:menuTitle="activeTopMenuTitle"
|
||||
:menuTree="activeMenuTree"
|
||||
:activeId="activeRouteId"
|
||||
:currentPath="currentRoutePath"
|
||||
:asideWidth="layoutMode === 'mobile' ? 0 : ASIDE_W"
|
||||
:siderWidth="SUB_W"
|
||||
@route-click="onRouteClick"
|
||||
:width="SUB_W"
|
||||
@sub-click="onSubClick"
|
||||
/>
|
||||
|
||||
<!-- 右侧内容区 -->
|
||||
@@ -86,9 +87,12 @@ import {
|
||||
} from '@/layouts/admin/router/adminRoutes.uts'
|
||||
import type { TopMenu, MenuGroup, RouteRecord } from '@/layouts/admin/router/adminRoutes.uts'
|
||||
|
||||
import { MenuNode, settingSubSiderMenu } from '@/layouts/admin/router/settingSubSiderMenu.uts'
|
||||
|
||||
import {
|
||||
activeTopMenuId,
|
||||
activeRouteId,
|
||||
lastSubIdByMenu,
|
||||
tabs,
|
||||
isMainAsideCollapsed,
|
||||
showSubSider,
|
||||
@@ -123,20 +127,66 @@ const isSubSiderOverlay = computed<boolean>(() => {
|
||||
return layoutMode.value === 'tablet'
|
||||
})
|
||||
|
||||
// 当前路由的路径
|
||||
const currentRoutePath = computed<string>(() => {
|
||||
const route = findRouteById(activeRouteId.value)
|
||||
return route ? route.path : ''
|
||||
})
|
||||
|
||||
// 将旧的 Group + Routes 转换为新的 Tree 结构 (支持 3 级嵌套)
|
||||
const activeMenuTree = computed<MenuNode[]>(() => {
|
||||
if (activeTopMenuId.value === 'setting') {
|
||||
return settingSubSiderMenu
|
||||
}
|
||||
|
||||
const tree: MenuNode[] = []
|
||||
activeGroups.value.forEach(group => {
|
||||
const routes = activeRoutes.value.get(group.id)
|
||||
if (routes != null && routes.length > 0) {
|
||||
const children: MenuNode[] = []
|
||||
routes.forEach(route => {
|
||||
children.push({
|
||||
id: route.id,
|
||||
title: route.title,
|
||||
type: 'page',
|
||||
path: route.path
|
||||
} as MenuNode)
|
||||
})
|
||||
|
||||
// 1:1 复刻 CRMEB: 如果分组标题为空,直接将子菜单平铺在顶层,不渲染分组行
|
||||
if (group.title === '') {
|
||||
children.forEach(child => {
|
||||
tree.push(child)
|
||||
})
|
||||
} else {
|
||||
tree.push({
|
||||
id: group.id,
|
||||
title: group.title,
|
||||
type: 'group',
|
||||
children: children
|
||||
} as MenuNode)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return tree
|
||||
})
|
||||
|
||||
/**
|
||||
* 核心逻辑:二级菜单是否可见
|
||||
* 1. 如果有子菜单内容 (activeGroups > 0)
|
||||
* 1. 如果有子菜单内容 (activeMenuTree > 0)
|
||||
* 2. 如果是 Desktop 且 showSubSider 为真 (Dock模式)
|
||||
* 3. 如果是 Tablet/Mobile 且 isOverlayVisible 为真 (Overlay模式)
|
||||
*/
|
||||
const isSubSiderVisible = computed<boolean>(() => {
|
||||
if (activeGroups.value.length === 0) return false
|
||||
// 首页模块通常不显示 SubSider
|
||||
if (activeTopMenuId.value === 'home' || activeMenuTree.value.length === 0) return false
|
||||
|
||||
if (layoutMode.value === 'desktop') {
|
||||
return showSubSider.value
|
||||
}
|
||||
|
||||
// Tablet 和 Mobile 模式下,作为 Overlay 受 isOverlayVisible 控制
|
||||
// Tablet 和 Mobile 模式下,直接由 isOverlayVisible 控制
|
||||
return isOverlayVisible.value
|
||||
})
|
||||
|
||||
@@ -155,7 +205,7 @@ const mainLeft = computed<string>(() => {
|
||||
let left = ASIDE_W // 只要不是 Mobile,主侧栏 70px 始终 Dock
|
||||
|
||||
// 只有在 Desktop 模式且二级菜单处于 Dock 模式显示时,才累加宽度
|
||||
if (layoutMode.value === 'desktop' && showSubSider.value && activeGroups.value.length > 0) {
|
||||
if (layoutMode.value === 'desktop' && showSubSider.value && activeMenuTree.value.length > 0) {
|
||||
left += SUB_W
|
||||
}
|
||||
|
||||
@@ -199,6 +249,24 @@ const currentComponent = computed<any>(() => {
|
||||
return getComponent(route.componentKey)
|
||||
})
|
||||
|
||||
// 监听路由变化,同步状态 (处理从 Tabs 或外部跳转的情况)
|
||||
watch(() => activeRouteId.value, (newId) => {
|
||||
const route = findRouteById(newId)
|
||||
if (route && route.parentId) {
|
||||
// 同步一级菜单
|
||||
if (activeTopMenuId.value !== route.parentId) {
|
||||
activeTopMenuId.value = route.parentId
|
||||
}
|
||||
// 同步最后访问记录
|
||||
lastSubIdByMenu.value[route.parentId] = newId
|
||||
|
||||
// 如果是 Desktop 且 SubSider 关着,自动打开(除非用户手动关了)
|
||||
if (layoutMode.value === 'desktop' && !showSubSider.value) {
|
||||
showSubSider.value = true
|
||||
}
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// ============================================
|
||||
// 事件处理
|
||||
// ============================================
|
||||
@@ -206,27 +274,46 @@ const currentComponent = computed<any>(() => {
|
||||
function onTopMenuClick(menu: TopMenu): void {
|
||||
activeTopMenuId.value = menu.id
|
||||
|
||||
// 1:1 复刻 CRMEB 交互:
|
||||
// 1. 如果有子菜单:Desktop 下 dock,Tablet/Mobile 下唤起 Overlay
|
||||
if (menu.groups.length > 0) {
|
||||
if (layoutMode.value === 'desktop') {
|
||||
showSubSider.value = true
|
||||
} else {
|
||||
isOverlayVisible.value = true
|
||||
}
|
||||
// 1:1 复刻 CRMEB 交互:点击 Aside 立即展示 SubSider
|
||||
if (layoutMode.value === 'desktop') {
|
||||
showSubSider.value = true
|
||||
} else {
|
||||
// 2. 如果没有子菜单:直接跳转并关闭所有 Overlay
|
||||
isOverlayVisible.value = true
|
||||
isMobileMenuOpen.value = false // 如果主侧栏开着,关掉它,开二级
|
||||
}
|
||||
|
||||
// 1:1 复刻 CRMEB 交互:点击一级菜单后,自动跳转到上一次记录的子页面或第一个子页面
|
||||
let targetId = lastSubIdByMenu.value[menu.id]
|
||||
if (!targetId && activeMenuTree.value.length > 0) {
|
||||
const firstNode = activeMenuTree.value[0]
|
||||
if (firstNode.type === 'page') {
|
||||
targetId = firstNode.id
|
||||
} else if (firstNode.children != null && firstNode.children!.length > 0) {
|
||||
targetId = firstNode.children![0].id
|
||||
}
|
||||
}
|
||||
|
||||
if (targetId) {
|
||||
openRoute(targetId)
|
||||
} else {
|
||||
// 兜底逻辑:如果没有二级菜单,跳转到 index
|
||||
openRoute(menu.id + '_index')
|
||||
closeAllMenu()
|
||||
}
|
||||
}
|
||||
|
||||
function onRouteClick(routeId: string): void {
|
||||
openRoute(routeId)
|
||||
// 1:1 复刻 CRMEB:在移动端或平板叠加模式下,点击具体子路由后自动收起
|
||||
if (layoutMode.value !== 'desktop') {
|
||||
closeAllMenu()
|
||||
}
|
||||
function onSubClick(payload: { id: string, path: string }): void {
|
||||
// CRMEB 跳转逻辑
|
||||
openRoute(payload.id)
|
||||
|
||||
// 更新最后访问记录
|
||||
lastSubIdByMenu.value[activeTopMenuId.value] = payload.id
|
||||
|
||||
// 重要:1:1 复刻 CRMEB,点击二级菜单项后,SubSider 通常保持显示状态
|
||||
// 只有在 Mobile 模式下,用户如果是想“跳转并收起”,通常需要点击遮罩或关闭按钮,但这里我们按桌面版常驻逻辑
|
||||
// 如果需要移动端点击自动关闭,才解开下面注释
|
||||
// if (layoutMode.value === 'mobile') {
|
||||
// closeAllMenu()
|
||||
// }
|
||||
}
|
||||
|
||||
function onTabClick(tab: TabItem): void {
|
||||
|
||||
Reference in New Issue
Block a user