Skip to content

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:mode toggle
  • ✅ 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(通知 + 头像)+ #footer slot 可覆盖
  • 📌 用真浏览器调 viewport 测试三形态:≥1200 PC / 760-1199 Pad / <760 Phone(mode="auto" 默认)

Live Demo · PC 形态

点 Parent 展开 / 点 Child 选中,看 Parent 自动变白粗体(联动)。

Live 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。

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。

行为规则

  1. 单组展开:同一时间只有一个 Parent 处于 expanded。点新 Parent → 旧的自动收起。
  2. Parent 自动 active-white:当前 activeKey 是某 Parent 的 child,该 Parent 自动显示为 active="white"(粗体白字)。
  3. 顶层叶节点:Parent 没有 children 时,点它直接 active="green",触发 update:activeKey
  4. Hover / Pressed:跟随 CSS native,不需要 prop。
  5. 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>

默认渲染通知 + 头像两个 trans icon-only button。如需替换,传 #footer slot:

vue
<Navigation :brand="{ tenant: '...' }" :items="items">
  <template #footer>
    <YourCustomFooter />
  </template>
</Navigation>

Props

Prop类型默认说明
brand{ tenant: string }必传。tenant 为竖排公司/租户名(仅 PC 形态渲染)
itemsNaviItemData[]必传。导航项数组(见下方类型)
activeKeystring''controlled。当前选中项的 key
expandedKeystring''当前展开的 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 让消费方切换
ts
interface NaviItemData {
  key: string
  label: string
  icon?: string                   // 仅 parent 顶层项有效
  badge?: boolean | BadgeColor    // 同 Item.badge
  children?: NaviItemData[]       // 仅 parent 顶层项有效
  disabled?: boolean
}

Events

EventPayload说明
update:activeKeystring用户选中新项(含 child)
update:expandedKeystring用户切换展开的 parent(空串 = 全部收起)
update:mode'auto' | 'pc' | 'pad'navi-collapsed-button click 时 emit 反向形态(v1.5)

Slots

Slot说明
footer替换默认的「通知 + 头像」footer