11 KiB
11 KiB
确保 Admin 页面侧边栏一直显示的完整步骤
概述
确保 uni-app-x 的 Admin 页面在切换过程中保持侧边栏显示需要从多个维度进行配置。以下是完整的步骤检查清单。
第一部分:文件和路由配置
1.1 创建新的 Admin 页面文件
文件路径: pages/mall/admin/maintain/<category>/<page-name>.uvue
重点:
- ✅ 使用 UTF-8 编码(不要 BOM)
- ✅ 严格的 SFC 结构:
<template>→<script>→<style> - ✅ 没有额外内容在closing tags之后
模板:
<template>
<AdminLayout currentPage="<page-id>">
<view class="page">
<!-- 页面内容 -->
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.page {
padding: 20px;
}
</style>
1.2 在 pages.json 中注册路由
位置: pages.json 中的合适分类下
格式:
{
"path": "maintain/<category>/<page-name>",
"style": {
"navigationBarTitleText": "页面标题",
"navigationStyle": "custom"
}
}
重点:
- ✅
navigationStyle: "custom"必须设置,允许自定义 AdminLayout - ✅
path必须与文件结构匹配
第二部分:菜单配置
2.1 在 menu.uts 中定义菜单项
文件: layouts/admin/utils/menu.uts
两种菜单结构:
选项 A:有子菜单的菜单组(推荐)
{
id: 'maintain',
title: '维护',
icon: '/static/maintain.svg',
path: '/pages/mall/admin/maintain/...',
groups: [
{
id: 'dev-config',
title: '开发配置',
children: [
{
id: 'module-config',
title: '模块配置',
path: '/pages/mall/admin/maintain/dev-config/module-config'
}
]
}
]
}
选项 B:没有子菜单的菜单组(叶子节点)
{
id: 'maintain',
title: '维护',
groups: [
{
id: 'system-info',
title: '系统信息',
path: '/pages/mall/admin/maintain/system-info',
children: [] // ⚠️ 必须显式设置为空数组
}
]
}
重点:
- ✅ 每个 menu item 必须有唯一的
id - ✅ 如果是叶子节点,必须显式设置
children: [] - ✅
path必须与 pages.json 路由匹配
2.2 菜单 ID 命名规则
建议:
一级菜单: maintain / user / order / product
二级组: dev-config / security / data / external
子项: module-config / permission / cron-job
currentPage 值应该与 menu.id 对应:
- 对应一级: currentPage="maintain"
- 对应二级: currentPage="system-info"
- 对应三级: currentPage="module-config"
第三部分:导航逻辑
3.1 nav.uts 匹配规则
文件: layouts/admin/utils/nav.uts
关键函数: findActiveByCurrentPage(menuList, currentPage)
匹配顺序 (必须按此顺序):
- 一级菜单 ID 匹配:
m.id === currentPage - 二级菜单组 ID 匹配:
g.id === currentPage⚠️ 包括叶子节点 - 二级菜单组 path 匹配:
normalize(g.path) === normalize(currentPage) - 三级菜单子项 ID 匹配:
c.id === currentPage - 三级菜单子项 path 匹配:
normalize(c.path) === normalize(currentPage) - 四级及以上: 递归查找
- 默认兜底: 返回
{ activeMenuId: 'home', activeSubId: '' }
核心代码:
// 关键:在检查 children 前,先检查 group 本身是否是叶子节点
for (const g of groups) {
if (g.id === page) {
return { activeMenuId: m.id, activeSubId: g.id }; // ✅ 叶子节点匹配
}
if (g.path && normalize(g.path) === pageNorm) {
return { activeMenuId: m.id, activeSubId: g.id }; // ✅ 叶子节点路径匹配
}
// 然后才检查 children
const cs = g.children ?? [];
// ...
}
3.2 页面中使用 currentPage
在页面文件中:
<AdminLayout currentPage="system-info">
<!-- 页面内容 -->
</AdminLayout>
currentPage 值确定规则:
- 如果页面是二级菜单组的叶子: 使用 group id (
system-info) - 如果页面是三级菜单子项: 使用 child id (
module-config) - 也可以使用路径形式 (
/pages/mall/admin/maintain/system-info)
第四部分:AdminLayout 组件
4.1 AdminLayout.uvue 的关键逻辑
文件: layouts/admin/AdminLayout.uvue
核心职责:
// 1. 导入必要的生命周期和工具
import { onShow } from "@dcloudio/uni-app";
import { findActiveByCurrentPage } from "./utils/nav.uts";
// 2. 接收 currentPage prop
const props = defineProps<{ currentPage: string }>();
// 3. 同步导航状态的关键函数
const syncActiveByCurrentPage = () => {
let current = props.currentPage;
if (!current) {
// 如果没有 currentPage,从路由获取
const pages = getCurrentPages();
const last = pages[pages.length - 1];
current = last?.route ? `/${last.route}` : "";
}
const r = findActiveByCurrentPage(menuList.value, current);
activeMenuId.value = r.activeMenuId; // ✅ 更新一级菜单
activeSubId.value = r.activeSubId; // ✅ 更新二级菜单
};
// 4. 在多个生命周期调用同步函数
watch(
() => props.currentPage,
() => syncActiveByCurrentPage(),
{ immediate: true },
);
onMounted(() => syncActiveByCurrentPage());
onShow(() => syncActiveByCurrentPage());
// 5. 计算二级侧边栏的内容
const activeGroups = computed(() => {
const m = menuList.value.find((it) => it.id === activeMenuId.value);
return m?.groups ?? [];
});
// 6. 根据 activeSubId 计算面包屑标题
const breadcrumb = computed(() => {
let subTitle = "";
const groups = activeGroups.value;
for (const g of groups) {
// ✅ 检查 group 本身(支持叶子节点)
if (g.id === activeSubId.value) {
subTitle = g.title;
break;
}
// ✅ 检查 group 的 children
const cs = g.children ?? [];
const hit = cs.find((c) => c.id === activeSubId.value);
if (hit) {
subTitle = hit.title;
break;
}
}
return subTitle
? `${activeMenuTitle.value} / ${subTitle}`
: activeMenuTitle.value;
});
渲染 AdminSubSider 时:
<AdminSubSider
v-if="activeGroups.length > 0"
:activeMenuTitle="activeMenuTitle"
:groups="activeGroups"
:activeSubId="activeSubId"
:activeMenuId="activeMenuId || 'home'"
@sub-click="onSubClick"
/>
第五部分:AdminSubSider 二级侧边栏
5.1 二级侧边栏的 groupAsChild 逻辑
文件: layouts/admin/components/AdminSubSider.uvue
关键逻辑:
// 支持 group 作为菜单项(叶子节点)的点击处理
const handleGroupTitleClick = (group: MenuGroup) => {
// 如果 group 有 path,直接导航
if (group.path) {
go(group.path)
}
// 否则选中这个 group
else {
activeSubId.value = group.id
}
}
// 模板中
<template v-for="group in groups">
<view
v-if="!group.children || group.children.length === 0"
class="group-as-child"
@click="handleGroupTitleClick(group)"
:class="{ active: activeSubId === group.id }"
>
{{ group.title }}
</view>
<view v-else class="group-normal">
<!-- 正常的组处理 -->
</view>
</template>
第六部分:状态管理(可选但推荐)
6.1 使用 state.uts 管理全局状态
文件: layouts/admin/state.uts
import { ref } from "vue";
// 跨页面持久化的状态
export const tabs = ref<TabItem[]>([]);
export const activeTabId = ref("");
export const isCollapsed = ref(false);
export const hasNotification = ref(false);
优点:
- ✅ 页面切换时保持侧边栏收起/展开状态
- ✅ 标签页状态持久化
- ✅ 通知状态保持
完整检查清单
📋 新增页面时必须检查:
-
文件
- 文件位置正确:
pages/mall/admin/maintain/<category>/<page-name>.uvue - 编码是 UTF-8(无 BOM)
- 正确的 SFC 结构
<script setup>导入了AdminLayout
- 文件位置正确:
-
路由配置
- 在
pages.json中注册了路由 navigationStyle: "custom"设置正确path与文件结构匹配
- 在
-
菜单配置
- 在
menu.uts中定义了菜单项 - menu id 唯一且命名规范
- 如果是叶子节点,设置
children: [] - 路径与 pages.json 匹配
- 在
-
页面文件
currentPageprop 值正确currentPage与菜单配置中的 id 对应
-
缓存清理
- 删除
unpackage/dist - 删除
.hbuilderx/cache - 清理浏览器缓存 (Ctrl+Shift+Delete)
- 强制刷新页面 (Ctrl+Shift+R)
- 删除
调试技巧
验证导航匹配
node test-system-info-nav.js
浏览器控制台诊断
// 在浏览器 DevTools 中运行
const pages = getCurrentPages();
const route = pages[pages.length - 1]?.route;
console.log("当前路由:", route);
// 查看 AdminLayout 组件状态
// 打开 Vue DevTools 查看 activeMenuId, activeSubId
常见问题排查
| 问题 | 原因 | 解决 |
|---|---|---|
| 侧边栏不显示 | currentPage 未匹配到菜单 |
检查 nav.uts 匹配逻辑 |
| 高亮错误 | menu.id 不一致 | 确保 currentPage 与 menu id 相同 |
| 页面切换闪烁 | 缓存问题 | 清理缓存,强制刷新 |
| 编译错误 | 文件编码问题 | 重新创建文件,确保无 BOM |
| 组件无法解析 | @ 别名未配置 | 检查 tsconfig.json 的 paths |
总结:一句话版本
确保 Admin 页面侧边栏一直显示的关键是:
正确配置菜单结构 → 准确设置 currentPage → 实现导航匹配逻辑 → 同步状态到 AdminLayout → 支持叶子节点处理
参考文件
| 文件 | 职责 |
|---|---|
pages/mall/admin/maintain/<category>/<page-name>.uvue |
页面文件 |
pages.json |
路由配置 |
layouts/admin/utils/menu.uts |
菜单定义 |
layouts/admin/utils/nav.uts |
导航匹配逻辑 |
layouts/admin/AdminLayout.uvue |
布局容器 |
layouts/admin/components/AdminSubSider.uvue |
二级侧边栏 |
layouts/admin/state.uts |
全局状态 |
tsconfig.json |
编译配置 |