主题
Navigation 侧边导航
响应式侧边栏导航。三种形态:
- PC ≥1200px:240px 完整展开(60px 竖排品牌列 + 1px divider + 180px 导航项列)
- Pad 760-1199px:60px 收起 icon-only + popout menu(v1.2 / v1.4)
- Phone <760px:56px top-bar + 全屏 drawer 抽屉(v1.3)
实现范围
- ✅ PC 形态(1200px ~ ∞):单组展开 / Parent active-white 自动联动 / Disabled 项
- ✅ Pad 形态(760-1199px):icon-only 收起 + expand toggle stub + 自动响应式切换
- ✅ navi-collapsed-button(v1.5):浮在 sider 右缘 ±16,click 触发
update:modetoggle - ✅ Badge 联动动画(v1.1):parent expand → badge fade-out;collapse → fade-in;child group fade-in/out
- ✅ Pad popout menu(v1.4):click 有 children 的 Pad parent → 浮出 192×fill child 菜单;ESC / 点外部 / 形态切换 / 切 active 后自动关闭;popout 期间该 parent badge fade-out
- ✅ PC↔Pad 切换动画(v1.6 Stage 1):双分支 absolute overlay + opacity fade(150ms)+ shadow 跟随 fade;9-element 精细化时间表(PRD §12.3)留 Stage 2 polish
- ✅ Phone 形态(v1.3):56px top-bar + 全屏 drawer(Teleport to body,slide-in from left);backdrop click / ESC / 选 child / 切形态 后自动关闭
- ✅ 内置 footer(通知 + 头像)+
#footerslot 可覆盖 - 📌 用真浏览器调 viewport 测试三形态:≥1200 PC / 760-1199 Pad / <760 Phone(
mode="auto"默认)
Live Demo · PC 形态
点 Parent 展开 / 点 Child 选中,看 Parent 自动变白粗体(联动)。
activeKey: dinersexpandedKey: peopleLive Demo · Pad 形态(v1.2 + v1.4 popout)
宽 60px sider(demo 容器宽 280 留 popout 浮出空间)。click 有 children 的 parent(如人员管理)→ 右侧浮出 192px popout menu。点 menu 外 / ESC / 切 active 自动关。选中 child 时其 parent 显绿,popout 期间 parent badge fade-out。
activeKey: diners点 expand toggle(第 2 个 icon)→ console.warn(v1.4 实装)
mode prop
默认 mode="auto":≥1200 显 PC,760-1199 显 Pad,跟随 viewport。
显式传 mode="pc" / mode="pad" 可强制单形态(docs / 测试 / 嵌入场景常用)。
内部结构
PC 形态(240px)
240
├── 60px side-bar
│ ├── <Logo variant="vertical" /> (32×164)
│ ├── tenant 竖排(writing-mode: vertical-rl)
│ └── footer slot(默认通知 + 头像)
├── 1px divider
└── 180px menu (8px L/R padding → items 164px wide)
├── Parent item (44 高)
└── Child items (40 高,仅父项 expanded 时渲染)Pad 形态(60px · v1.2)
60
├── pad-header 60×16 装饰条
├── <Logo variant="mark" /> 32×32
├── pad-menu (icon-only items, gap 12, pad-bottom 40)
│ ├── expand toggle (v1.4 stub) 44×44 ← console.warn
│ └── 顶层 items 1..N 44×44
└── pad-footer 60×132 (通知 + 头像,同 PC)
右侧附 box-shadow: var(--mc-shadow-navi)关键差异(vs PC):
- Logo 切到
variant="mark"32×32(不再是 vertical 164 高 SVG) - corps-name tenant 竖排在 Pad 隐藏
- items 用 trans icon-only Button + Badge 6×6 自渲染(不复用
<Item>) - active 状态简化到
'off' | 'green',选中 child 时其 parent 自动显绿
Phone 形态(top-bar + drawer · v1.3)
viewport <760
├── navi-top-mobile 100vw × 56 top-bar 占顶
│ ├── button-menu 40×32 汉堡 (左)
│ ├── flex spacer
│ ├── button-notification 40×32 + 红 6×6 dot
│ └── button-user 40×32 头像
└── (内容区在 top-bar 下方)
drawer (隐藏,按 button-menu 触发):
├── backdrop 100vw × 100vh rgba(0,0,0,0.45) overlay
└── drawer 240×100vh from left translateX(-100% → 0)
内含 PC 完整 sider 视觉 (mark + tenant + menu + footer)触发与关闭:button-menu click 打开 drawer;backdrop click / ESC / 选 child / 形态切换 → 关闭。 动画:backdrop opacity fade 150ms / drawer translateX(-100%→0) 150ms ease-in-out。
行为规则
- 单组展开:同一时间只有一个 Parent 处于 expanded。点新 Parent → 旧的自动收起。
- Parent 自动 active-white:当前
activeKey是某 Parent 的 child,该 Parent 自动显示为active="white"(粗体白字)。 - 顶层叶节点:Parent 没有 children 时,点它直接
active="green",触发update:activeKey。 - Hover / Pressed:跟随 CSS native,不需要 prop。
- Disabled:item data 里
disabled: true,pointer-events:none + 灰态。
路由判定(controlled)
组件本身不耦合 vue-router。消费方算出当前 activeKey 传入,组件只负责渲染。
vue
<script setup>
import { useRoute, useRouter } from 'vue-router'
import { computed } from 'vue'
const route = useRoute()
const router = useRouter()
const activeKey = computed(() => route.meta.naviKey)
</script>
<template>
<Navigation
:brand="{ tenant: '满天堂游戏' }"
:items="items"
:active-key="activeKey"
@update:active-key="key => router.push({ name: keyToRoute(key) })"
/>
</template>用法
vue
<script setup>
import { ref } from 'vue'
const activeKey = ref('home')
const items = [
{ key: 'home', label: '首页', icon: 'general/home' },
{
key: 'people', label: '人员管理', icon: 'user/user-group', badge: true,
children: [
{ key: 'diners', label: '用餐人员' },
{ key: 'depts', label: '部门管理', badge: true },
{ key: 'invites', label: '邀请链接' },
],
},
{ key: 'orders', label: '订单管理', icon: 'general/order' },
{ key: 'settings', label: '企业设置', icon: 'general/setting' },
]
</script>
<template>
<Layout mode="pc">
<template #navigation>
<Navigation
:brand="{ tenant: '满天堂游戏' }"
:items="items"
v-model:active-key="activeKey"
/>
</template>
<!-- main content -->
</Layout>
</template>自定义 Footer
默认渲染通知 + 头像两个 trans icon-only button。如需替换,传 #footer slot:
vue
<Navigation :brand="{ tenant: '...' }" :items="items">
<template #footer>
<YourCustomFooter />
</template>
</Navigation>Props
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
brand | { tenant: string } | — | 必传。tenant 为竖排公司/租户名(仅 PC 形态渲染) |
items | NaviItemData[] | — | 必传。导航项数组(见下方类型) |
activeKey | string | '' | controlled。当前选中项的 key |
expandedKey | string | '' | 当前展开的 parent key(仅 PC 形态消费) |
mode | 'auto' | 'pc' | 'pad' | 'phone' | 'auto' | 形态。'auto' = @media 自动切换(≥1200 PC / 760-1199 Pad / <760 Phone);显式 mode 强制单形态。支持 v-model:mode —— navi-collapsed-button click 时 emit update:mode 让消费方切换 |
NaviItemData
ts
interface NaviItemData {
key: string
label: string
icon?: string // 仅 parent 顶层项有效
badge?: boolean | BadgeColor // 同 Item.badge
children?: NaviItemData[] // 仅 parent 顶层项有效
disabled?: boolean
}Events
| Event | Payload | 说明 |
|---|---|---|
update:activeKey | string | 用户选中新项(含 child) |
update:expandedKey | string | 用户切换展开的 parent(空串 = 全部收起) |
update:mode | 'auto' | 'pc' | 'pad' | navi-collapsed-button click 时 emit 反向形态(v1.5) |
Slots
| Slot | 说明 |
|---|---|
footer | 替换默认的「通知 + 头像」footer |