修改页面结构

This commit is contained in:
2026-02-02 20:07:37 +08:00
parent 3de5e9ebe9
commit 21f4a0fa96
209 changed files with 41824 additions and 2730 deletions

View File

@@ -0,0 +1,422 @@
# 确保 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之后
**模板**:
```uvue
<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` 中的合适分类下
**格式**:
```json
{
"path": "maintain/<category>/<page-name>",
"style": {
"navigationBarTitleText": "页面标题",
"navigationStyle": "custom"
}
}
```
**重点**:
-`navigationStyle: "custom"` 必须设置,允许自定义 AdminLayout
-`path` 必须与文件结构匹配
---
## 第二部分:菜单配置
### 2.1 在 menu.uts 中定义菜单项
**文件**: `layouts/admin/utils/menu.uts`
**两种菜单结构**:
#### 选项 A有子菜单的菜单组推荐
```typescript
{
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没有子菜单的菜单组叶子节点
```typescript
{
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)`
**匹配顺序** (必须按此顺序):
1. 一级菜单 ID 匹配: `m.id === currentPage`
2. 二级菜单组 ID 匹配: `g.id === currentPage` ⚠️ **包括叶子节点**
3. 二级菜单组 path 匹配: `normalize(g.path) === normalize(currentPage)`
4. 三级菜单子项 ID 匹配: `c.id === currentPage`
5. 三级菜单子项 path 匹配: `normalize(c.path) === normalize(currentPage)`
6. 四级及以上: 递归查找
7. 默认兜底: 返回 `{ activeMenuId: 'home', activeSubId: '' }`
**核心代码**:
```typescript
// 关键:在检查 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
**在页面文件中**:
```uvue
<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`
**核心职责**:
```typescript
// 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 时**:
```uvue
<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`
**关键逻辑**:
```typescript
// 支持 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`
```typescript
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 匹配
- [ ] **页面文件**
- [ ] `currentPage` prop 值正确
- [ ] `currentPage` 与菜单配置中的 id 对应
- [ ] **缓存清理**
- [ ] 删除 `unpackage/dist`
- [ ] 删除 `.hbuilderx/cache`
- [ ] 清理浏览器缓存 (Ctrl+Shift+Delete)
- [ ] 强制刷新页面 (Ctrl+Shift+R)
---
## 调试技巧
### 验证导航匹配
```powershell
node test-system-info-nav.js
```
### 浏览器控制台诊断
```javascript
// 在浏览器 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` | 编译配置 |