完善页面布局

This commit is contained in:
2026-02-06 10:14:46 +08:00
parent 7a75ab7df4
commit 9eaf5c1a64
28 changed files with 1166 additions and 177 deletions

View File

@@ -11,6 +11,7 @@ import {
buildDefaultTabs,
getTopMenus
} from '@/layouts/admin/router/adminRoutes.uts'
import { addView, activeFullPath, visitedViews } from './tagsViewStore.uts'
/**
* 标签页类型
@@ -32,6 +33,12 @@ export const activeTopMenuId = ref<string>('home')
/** 当前激活的路由ID */
export const activeRouteId = ref<string>('home_index')
/** 记录每个一级模块上一次访问的二级路由ID (CRMEB 体验增强) */
export const lastSubIdByMenu = ref<Record<string, string>>({})
/** 标记是否由用户手动关闭了 SubSider (移动端) */
export const isManualClosed = ref<boolean>(false)
/** 打开的标签页列表 */
export const tabs = ref<TabItem[]>([])
@@ -82,6 +89,8 @@ export function openRoute(routeId: string, addTab: boolean = true): void {
// 更新一级菜单选中态
if (route.parentId) {
activeTopMenuId.value = route.parentId
// 记录该模块最后访问的子路由
lastSubIdByMenu.value[route.parentId] = routeId
} else {
// 首页等顶级路由
activeTopMenuId.value = routeId.split('_')[0]
@@ -116,6 +125,10 @@ function addTabItem(route: RouteRecord): void {
isAffix: route.isAffix || false
})
}
// 更新新版 TagsViewStore
addView(route, route.path)
activeFullPath.value = route.path
}
/**
@@ -201,6 +214,11 @@ export function initNavState(): void {
isAffix: r.isAffix || false
}))
// 初始化 TagsViewStore
defaultTabs.forEach(r => {
addView(r, r.path)
})
// 打开首页
openRoute('home_index', false)
}

View File

@@ -0,0 +1,126 @@
/**
* TagsView 状态管理
* 复刻 CRMEB 风格的标签栏逻辑
*/
import { ref } from 'vue'
import type { RouteRecord } from '@/layouts/admin/router/adminRoutes.uts'
/**
* 标签页视图类型
*/
export type TagView = {
id: string // 对应路由ID
title: string // 标题
path: string // 基础路径
fullPath: string // 完整路径(包含参数)
name: string // 组件Key
meta: {
affix?: boolean
noCache?: boolean
}
}
/** 访问过的页面列表 */
export const visitedViews = ref<TagView[]>([])
/** 缓存的页面列表 (用于 keep-alive) */
export const cachedViews = ref<string[]>([])
/** 开启的标签页详情 */
export const activeFullPath = ref<string>('')
// ============================================
// Actions
// ============================================
/**
* 添加视图
*/
export function addView(route: RouteRecord, fullPath: string): void {
// 1. 添加到访问列表
if (visitedViews.value.some(v => v.fullPath === fullPath)) return
// 如果 ID 相同但 fullPath 不同(参数变化), 则更新或者替换
const existingIndex = visitedViews.value.findIndex(v => v.id === route.id)
const newView: TagView = {
id: route.id,
title: route.title,
path: route.path,
fullPath: fullPath,
name: route.componentKey,
meta: {
affix: route.isAffix || false,
noCache: false // 默认为缓存,如果有特殊需求可扩展
}
}
if (existingIndex > -1) {
// 同一路由不同参数, 更新记录
visitedViews.value[existingIndex] = newView
} else {
visitedViews.value.push(newView)
}
// 2. 添加到缓存列表
if (!route.componentKey) return
if (cachedViews.value.includes(route.componentKey)) return
cachedViews.value.push(route.componentKey)
}
/**
* 删除视图
*/
export function delView(view: TagView): void {
const index = visitedViews.value.findIndex(v => v.fullPath === view.fullPath)
if (index > -1 && !visitedViews.value[index].meta.affix) {
visitedViews.value.splice(index, 1)
}
const cacheIndex = cachedViews.value.indexOf(view.name)
if (cacheIndex > -1) {
cachedViews.value.splice(cacheIndex, 1)
}
}
/**
* 关闭其他标签
*/
export function delOthersViews(view: TagView): void {
visitedViews.value = visitedViews.value.filter(v => v.meta.affix || v.fullPath === view.fullPath)
cachedViews.value = visitedViews.value.map(v => v.name)
}
/**
* 关闭右侧标签
*/
export function delRightTags(view: TagView): void {
const index = visitedViews.value.findIndex(v => v.fullPath === view.fullPath)
if (index === -1) return
visitedViews.value = visitedViews.value.filter((v, i) => {
return i <= index || v.meta.affix
})
cachedViews.value = visitedViews.value.map(v => v.name)
}
/**
* 关闭所有
*/
export function delAllViews(): void {
const affixTags = visitedViews.value.filter(v => v.meta.affix)
visitedViews.value = affixTags
cachedViews.value = affixTags.map(v => v.name)
}
/**
* 刷新当前视图 (通常结合全局事件通知组件)
*/
export function refreshView(view: TagView): void {
const index = cachedViews.value.indexOf(view.name)
if (index > -1) {
cachedViews.value.splice(index, 1)
}
// 通知框架重新加载该组件的逻辑通常在组件内部处理或通过 key 变化
}