shared-layout-components

基于 Vue3 和 Ant Design Vue 的公共布局组件库
6,300
Install
npm install shared-layout-components
Latest Version:1.0.38
Vue:3
License:MIT
Last Updated:Jul 28, 2026
Links: npm
Maintainer: boice_cc

shared-layout-components

基于 Vue3 和 Ant Design Vue 的公共布局组件库,提供完整的后台管理系统布局解决方案。

特性

  • 🎯 开箱即用 - 提供完整的后台布局组件
  • 🔗 联动交互 - 菜单、标签页、面包屑自动联动
  • 💾 状态持久化 - 基于 Pinia 的标签页状态管理,支持刷新保持
  • 🎨 高度可定制 - 支持插槽自定义各区域内容
  • 📦 按需引入 - 支持 UMD 和 ES Module 两种格式
  • 🔄 对等依赖 - 通过函数参数接收宿主应用的依赖实例,避免重复打包
  • 🔌 微前端支持 - 内置 qiankun 微应用生命周期支持

组件列表

组件 说明
PageLayout 主布局容器,整合侧边栏、标签页、面包屑和内容区域
SidebarLayout 侧边栏组件,支持菜单折叠和层级展开
Tabs 标签页组件,支持关闭和切换
BreadCrumb 面包屑组件,自动根据当前菜单生成路径

安装

npm install shared-layout-components

前置依赖(对等依赖)

本库使用**对等依赖(peer dependencies)**方式管理依赖,需要在宿主应用中安装以下依赖:

npm install vue@^3.3.4 vue-router@^4.0.0 pinia@^3.0.0 ant-design-vue@^4.2.6 @ant-design/icons-vue@^7.0.0
{
  "vue": "^3.3.4",
  "vue-router": "^4.0.0",
  "pinia": "^3.0.0",
  "ant-design-vue": "^4.2.6",
  "@ant-design/icons-vue": "^7.0.0"
}

可选依赖

依赖 说明
pinia-plugin-persistedstate 标签页状态持久化,不传则状态在页面刷新后丢失

说明:本库不会将依赖打包到构建产物中,而是通过函数参数从宿主应用接收实例,避免版本冲突和重复打包。

快速开始

1. 定义路由配置

// router.js
export const routes = [
  {
    path: '/',
    redirect: '/dashboard'
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('./pages/Dashboard.vue'),
    meta: { title: '仪表盘' }
  },
  {
    path: '/user/list',
    name: 'UserList',
    component: () => import('./pages/UserList.vue'),
    meta: { title: '用户列表' }
  }
];

2. 初始化应用

// main.js
import { createApp } from 'vue';
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/reset.css';
import App from './App.vue';
import { routes } from './router';

// 导入对等依赖模块
import * as VueRouter from 'vue-router';
import * as pinia from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';

// 导入组件库
import { createSharedLayout } from 'shared-layout-components';

const app = createApp(App);

// 创建并安装 Pinia(宿主应用自行管理)
const piniaInstance = pinia.createPinia();
piniaInstance.use(piniaPluginPersistedstate);
app.use(piniaInstance);

// 创建并安装 Vue Router(宿主应用自行管理)
const router = VueRouter.createRouter({
  history: VueRouter.createWebHistory(),
  routes
});
app.use(router);

// 使用组件库,传入宿主应用的依赖实例
const sharedLayout = createSharedLayout({
  pinia,                      // 传入 pinia 模块
  piniaPluginPersistedstate,  // 传入持久化插件
  router,                     // 传入已创建的路由实例
  vueRouter: VueRouter        // 传入 vue-router 模块
});
app.use(sharedLayout);

app.use(Antd);
app.mount('#app');

说明:

  • 宿主应用需要自行创建并安装 router 和 pinia 实例
  • 将依赖模块通过参数传递给组件库
  • 组件库通过注入机制将依赖提供给内部组件使用
  • 这种方式避免了依赖重复打包和版本冲突

3. 使用 PageLayout 组件

<template>
  <PageLayout
    ref="pageLayoutRef"
    :menuData="menuData"
    :systemTitle="systemTitle"
  >
    <template #default="{ activeTab }">
      <router-view></router-view>
    </template>
  </PageLayout>
</template>

<script setup>
import { ref, provide } from 'vue';
import { PageLayout } from 'shared-layout-components';

const pageLayoutRef = ref(null);
const systemTitle = ref('管理系统');

const menuData = ref([
  {
    key: 'dashboard',
    label: '仪表盘',
    title: '仪表盘',
    path: '/dashboard'
  },
  {
    key: 'user',
    label: '用户管理',
    title: '用户管理',
    children: [
      { key: 'user-list', label: '用户列表', title: '用户列表', path: '/user/list' },
      { key: 'user-role', label: '角色管理', title: '角色管理', path: '/user/role' }
    ]
  }
]);

// 提供打开新 tab 的方法给子页面使用
provide('openTab', (tab) => {
  const layout = pageLayoutRef.value;
  if (layout) {
    layout.addTab(tab);
  }
});
</script>

组件会自动处理:

  • 路由变化时自动添加对应标签页
  • 点击菜单时自动跳转路由
  • 切换标签页时自动跳转路由
  • 关闭标签页时自动切换到相邻标签页并跳转

4. 子页面打开新 Tab 示例

<!-- UserList.vue -->
<template>
  <div class="page-user-list">
    <h2>用户列表</h2>
    <a-card>
      <div class="table-toolbar">
        <a-button type="primary" @click="handleAdd">
          <plus-outlined />
          新增用户
        </a-button>
      </div>
      <!-- 表格内容 -->
    </a-card>
  </div>
</template>

<script setup>
import { inject } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';

const openTab = inject('openTab', null);

const handleAdd = () => {
  if (openTab) {
    openTab({
      key: 'user-list-add',      // 唯一标识
      title: '新增用户',          // 标签页标题
      closable: true,            // 是否可关闭
      path: '/user/list/add'     // 路由路径
    });
  }
};
</script>

⚠️ 路由定义注意事项

子页面路由路径必须包含父级列表路径,以便组件库正确匹配左侧菜单和面包屑。

✅ 正确示例
// 菜单配置
const menuData = [
  {
    key: 'audiobook',
    label: '有声书管理',
    title: '有声书管理',
    children: [
      { 
        key: 'audiobook-list', 
        label: '有声书列表', 
        title: '有声书列表', 
        path: '/audiobook/index'  // 列表页路径
      }
    ]
  }
];

// 路由配置
const routes = [
  {
    path: '/audiobook/index',           // 列表页
    name: 'AudiobookList',
    component: () => import('./pages/AudiobookList.vue'),
    meta: { title: '有声书列表' }
  },
  {
    path: '/audiobook/index/add',       // 新增页 ✅ 包含列表路径
    name: 'AudiobookAdd',
    component: () => import('./pages/AudiobookAdd.vue'),
    meta: { title: '新增有声书' }
  },
  {
    path: '/audiobook/index/edit/:id',  // 编辑页 ✅ 包含列表路径
    name: 'AudiobookEdit',
    component: () => import('./pages/AudiobookEdit.vue'),
    meta: { title: '编辑有声书' }
  }
];
❌ 错误示例
// 错误:新增页路径没有包含列表路径
{
  path: '/audiobook/add',     // ❌ 错误:应该是 /audiobook/index/add
  name: 'AudiobookAdd',
  component: () => import('./pages/AudiobookAdd.vue')
}
路径匹配规则

组件库使用最长前缀匹配算法自动匹配菜单:

  1. 当前路由 /audiobook/index/add 会匹配到菜单 /audiobook/index
  2. 面包屑会显示:有声书管理 > 有声书列表 > 新增有声书
  3. 左侧菜单"有声书列表"会被自动选中并展开

建议路径命名规范:

页面类型 路径示例 说明
列表页 /module/index 基础路径
新增页 /module/index/add 列表路径 + /add
编辑页 /module/index/edit/:id 列表路径 + /edit/:id
详情页 /module/index/detail/:id 列表路径 + /detail/:id

API

createSharedLayout(options)

创建组件库插件,需要在宿主应用中调用。

参数 说明 类型 必需
pinia Pinia 模块(import * as pinia from 'pinia') Object 是
piniaPluginPersistedstate Pinia 持久化插件 Object 否
router 已创建的 Vue Router 实例 Object 否
vueRouter Vue Router 模块(import * as VueRouter from 'vue-router') Object 否
routes 路由配置数组(如未传入 router 则需要) Array 否
routerOptions 路由选项 Object 否

PageLayout Props

参数 说明 类型 默认值
menuData 菜单数据 Array []
systemTitle 系统标题 String '管理系统'
showSidebar 是否显示侧边栏 Boolean true
showTabs 是否显示标签页 Boolean true
showBreadcrumb 是否显示面包屑 Boolean true
autoAddTab 是否自动添加标签页 Boolean true
enableRouterSync 是否启用路由联动 Boolean true
defaultTabs 默认标签页列表 Array []
homeBreadcrumb 首页面包屑配置 Object {}

PageLayout Events

事件名 说明 回调参数
menu-click 菜单点击事件 (item: MenuItem)
tab-change 标签页切换事件 (key: String)
tab-click 标签页点击事件 (tab: Tab)
tab-close 标签页关闭事件 (tab: Tab)
update:activeTab 当前激活标签页变化 (key: String)
breadcrumb-click 面包屑点击事件 (data: Object)

PageLayout Methods

通过 ref 调用:

方法名 说明 参数
addTab 手动添加标签页,自动跳转路由 (tab: Tab)
removeTab 手动移除标签页,删除后自动切换到相邻标签页并跳转路由;仅剩一个标签页时不允许删除 (key: String)
addTabByMenuKey 根据菜单 key 添加标签页 (key: String)
getCurrentTab 获取当前标签页数据 -
clearTabs 清空所有标签页 -
findMenuItem 根据菜单 key 查找菜单项 (items: Array, key: String)
findMenuKeyByPath 根据路由路径查找菜单 key (items: Array, path: String)

暴露的响应式数据:

数据 说明 类型
tabsList 标签页列表 ComputedRef<Array>
currentTab 当前激活标签页 key ComputedRef<String>
currentTabData 当前标签页数据 ComputedRef<Object>
breadcrumbList 面包屑数据 ComputedRef<Array>

PageLayout Slots

插槽名 说明 参数
default 主内容区域 { activeTab, tabData }
sidebar 自定义侧边栏 -
tabs 自定义标签页 { tabs, activeTab }
breadcrumb 自定义面包屑 { items }
breadcrumbExtra 面包屑右侧扩展区域 -

菜单数据格式

const menuData = [
  {
    key: 'dashboard',      // 唯一标识
    label: '仪表盘',        // 菜单名称
    title: '仪表盘',        // 标签页标题
    icon: 'dashboard',     // 图标(可选)
    path: '/dashboard'     // 路由路径
  },
  {
    key: 'user',
    label: '用户管理',
    title: '用户管理',
    children: [            // 子菜单
      {
        key: 'user-list',
        label: '用户列表',
        title: '用户列表',
        path: '/user/list'
      }
    ]
  }
];

标签页数据格式

const tab = {
  key: 'dashboard',        // 唯一标识
  title: '仪表盘',          // 标签页标题
  path: '/dashboard',      // 路由路径
  closable: true           // 是否可关闭(默认 true)
};

Tabs 组件

独立的标签页组件,也可单独使用。

Tabs Props
参数 说明 类型 默认值
tabs 标签页数据列表 Array []
activeTab 当前激活标签页 key String ''
Tabs Events
事件名 说明 回调参数
update:activeTab 激活标签页变化 (key: String)
tab-click 标签页点击事件 (tab: Tab)
tab-close 标签页关闭事件 (tab: Tab)
Tabs Methods

通过 ref 调用:

方法名 说明 参数
removeTab 删除指定标签页 (key: String)
使用示例
<template>
  <Tabs
    ref="tabsRef"
    :tabs="tabsList"
    :activeTab="activeTab"
    @update:activeTab="onTabChange"
    @tab-close="onTabClose"
  />
</template>

<script setup>
import { ref } from 'vue';
import { Tabs } from 'shared-layout-components';

const tabsRef = ref();
const tabsList = ref([...]);
const activeTab = ref('');

// 通过 ref 删除指定 tab
const removeTabByKey = (key) => {
  tabsRef.value.removeTab(key);
};
</script>

状态管理

组件内部使用 Pinia 管理标签页状态,通过 getStores 可访问:

import { getStores } from 'shared-layout-components';

const { useTabsStore } = getStores();
const tabsStore = useTabsStore();

// 获取当前标签页列表
console.log(tabsStore.tabs);

// 获取当前激活标签页
console.log(tabsStore.currentTab);

// 获取当前标签页数据
console.log(tabsStore.currentTabData);

本地开发

# 安装依赖
npm install

# 启动开发服务器
npm run dev

# 构建生产版本
npm run build

发布

# 构建并发布
npm run prepublishOnly
npm publish

License

MIT

Related Packages

best-link-layout-xinlian

BestLink 后台管理系统布局组件

23,136
nav-layout-vue3

Vue 3 Navigation Layout Container Component Library

1,292
layout-vue3

简易布局

3,256 1
vega-ui

Vue 3 UI library for rapid prototyping.

8,691
vue3-admin-layout

一个灵活的 Vue 管理后台布局组件

3,929 2

Version History

Version Released Vue License
1.0.38 ^3.3.4 MIT
1.0.37 ^3.3.4 MIT
1.0.36 ^3.3.4 MIT
1.0.35 ^3.3.4 MIT
1.0.34 ^3.3.4 MIT