npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

shared-layout-components

v1.0.22

Published

基于 Vue3 和 Ant Design Vue 的公共布局组件库

Readme

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>

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