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

user-profile-grid-plugin

v1.1.2

Published

用户画像拖拽布局组件库 - 基于 GridStack 的低耦合可复用 Vue 3 组件。

Readme

user-profile-grid-plugin

用户画像拖拽布局组件库 - 基于 GridStack 的低耦合可复用 Vue 3 组件。

Features

  • 支持拖拽和大小调整的网格布局:基于 GridStack,面板可拖拽、可拉伸,支持锁定/解锁与关闭。
  • 灵活配置组件类型和布局模板:通过 menus + routes + widgets 动态映射可用模块;内置多种布局模板(默认/详细/简约)。
  • 低耦合设计,易于集成到任意项目:外部提供布局 JSON(字符串)与保存回调;内部只负责渲染/交互与布局变更同步。

核心组件

  • 主组件src/components/GridStackCom/KydUserProfileGrid.vue
  • 网格项包装src/components/GridStackCom/GridModel.vue
  • 面板容器(标题/菜单/更多跳转)src/components/GridStackCom/ContainerPanel.vue

依赖与环境

  • Vue: 3.x
  • 构建: Vite(本仓库为 demo/开发环境)
  • Grid 引擎: gridstack
  • UI: element-plus(下拉菜单、对话框、表单等)

快速开始(本仓库运行 demo)

npm install
npm run dev

demo 入口:src/components/HelloWorld.vue(通过 ref 初始化网格并传入 demo 数据)。

在你的项目中使用

通过 npm 安装

该仓库目前已支持 lib 模式 构建用于发布到 npm(npm run build:lib)。

npm i user-profile-grid-plugin

宿主项目还需要安装(或已安装)以下 peer 依赖:

npm i vue element-plus @element-plus/icons-vue gridstack

并在宿主项目入口引入 ElementPlus 样式(至少一次):

import "element-plus/dist/index.css";

还需要引入 GridStack 样式本组件库样式(否则网格/占位/拖拽手柄等会错乱):

import "gridstack/dist/gridstack.min.css";
import "user-profile-grid-plugin/style.css";

该组件本质上是一个“布局容器”,你需要提供 4 类输入:

  • widgets:可渲染业务模块的加载器集合(建议用 import.meta.glob 扫描 widgets/ 目录)
  • routes:路由树(用于从路由表中提取 fullPath,给面板“更多”跳转使用)
  • menus:可用模块清单(用于生成“添加组件”下拉选项)
  • layout:用户布局(JSON 字符串;网格项数组)

1) 准备 widgets(业务模块目录)

示例(与本仓库一致):

// 匹配所有的组件(宿主项目的业务模块)
const widgets = import.meta.glob("./widgets/**/*.vue");

要求:

  • key 必须包含 widgets/:组件内部用 path.split("widgets/") 解析模块路径。
  • menus[i].componentroutes[*].component 需要能匹配到 widgets/ 下的相对路径(不含 .vue),例如:system/user/index

2) 在页面中渲染并初始化

(参考本仓库 src/components/HelloWorld.vue

<template>
  <kyd-user-profile-grid
    ref="gridRef"
    v-if="layout"
    :proxy="proxy"
    :routes="routes"
    :layout="layout"
    :widgets="widgets"
    :menus="menus"
    :saveLayout="saveLayout"
    :router="router"
  />
</template>

<script setup>
import { getCurrentInstance, nextTick, onMounted, onBeforeUnmount, ref } from "vue";
import { KydUserProfileGrid } from "user-profile-grid-plugin";

const proxy = getCurrentInstance(); // 必传:用于 vNode.appContext 注入
const gridRef = ref(null);

// 你的项目里替换为真实数据
const routes = [];     // 路由树
const menus = ref([]); // 模块清单
const widgets = import.meta.glob("@/widgets/**/*.vue");
const router = null;   // 如果你希望“更多”跳转生效,传入 router 实例

const layout = ref(""); // 注意:必须是 JSON 字符串
const saveLayout = (items) => {
  // items 是网格项数组(包含 w/h/x/y/type/title/...),由组件在拖拽/缩放/增删后回调
  // 这里建议你持久化到后端/本地存储,然后重新喂回 layout
};

onMounted(async () => {
  layout.value = JSON.stringify([]); // 初次可为空数组
  await nextTick();
  gridRef.value?.loadGridLayout();
});

onBeforeUnmount(() => {
  gridRef.value?.destroyGridLayout();
});
</script>

Props(KydUserProfileGrid)

组件使用 defineProps(),以下字段为当前实现所需。

  • proxy: Object(必传)
    • 来自 getCurrentInstance(),用于给动态渲染的 Grid item vNode 注入 appContext
  • router: any(可选)
    • 透传给内部的 ContainerPanel,用于点击“更多”时执行 router.push(skipUrl)
  • routes: Array(必传)
    • 路由树结构;内部会递归提取 { fullPath, component } 映射。
  • layout: string(必传)
    • 用户布局配置 JSON 字符串。内容是网格项数组(示例见下文“Layout 数据结构”)。
  • widgets: Record<string, Function>(必传)
    • import.meta.glob() 的返回值;内部按 menus[i].component 匹配对应 loader 并 defineAsyncComponent
  • menus: Array(必传)
    • 可用模块清单(至少需要 menuId/menuName/component)。
  • saveLayout: (items: any[]) => void(必传)
    • 布局持久化回调:拖拽/缩放/新增/删除/套用模板时触发。

Expose(通过 ref 调用)

KydUserProfileGrid 暴露以下方法(defineExpose):

  • loadGridLayout()
    • 初始化:解析 routes/menus/widgets/layout,并创建 GridStack 实例,然后渲染网格项。
  • destroyGridLayout()
    • 销毁 GridStack 实例(建议在 onBeforeUnmount 调用)。
  • adjustGridLayoutSize()
    • 重新计算 cellHeight(组件内部也会监听 window.resize,你也可以在容器尺寸变化时手动调用)。

Layout 数据结构

layout 需要是 JSON 字符串,内容是一个数组,每个元素代表一个网格项。典型字段:

  • id: string 网格项唯一 id(用于 gs-id
  • w/h/x/y: number 网格的宽高与坐标(按 12 列计算)
  • type: string 业务组件标识(例如 system/user/index
  • title: string 面板标题
  • menuId: number|string 关联菜单 id(用于回填)
  • disableResize: boolean 是否禁用拉伸
  • isLocked: boolean 是否锁定(锁定后不可拖拽)
  • skipUrl: string “更多”跳转地址(由 routes 推导得到)
  • data: object 透传给插槽内容的业务数据(当前实现用于 slot 渲染时展开)

本仓库 demo 数据可参考:src/assets/data/layout.json

交互说明(面板菜单)

每个网格项由 GridModel -> ContainerPanel 渲染,默认菜单包含:

  • 编辑:打开配置弹窗,可修改标题/模块类型/宽高/禁用拉伸/锁定
  • 锁定/解锁:切换锁定状态,并更新 GridStack 的 noMove/locked
  • 关闭:删除网格项并触发 saveLayout

当点击菜单出现非默认 key 时,会以 custom 事件透传(当前实现会在控制台打印)。

常见注意点

  • 必须传 proxy:内部动态 render(vNode, element) 时会用到 proxy.appContext,否则会抛错。
  • 必须手动调用 loadGridLayout():组件 onMounted 不会自动初始化,建议 nextTick 后通过 ref 调用。
  • layout 是字符串不是对象:请 JSON.stringify(items) 传入;组件内部用 JSON.parse(layout) 读取。
  • widgets 的路径约定:内部通过 path.split("widgets/")[1] 匹配模块名,请保持目录约定一致或按需改造匹配逻辑。

项目结构(与集成相关)

  • src/components/GridStackCom/KydUserProfileGrid.vue:GridStack 初始化与布局管理
  • src/components/GridStackCom/GridModel.vue:网格项包装,统一菜单事件
  • src/components/GridStackCom/ContainerPanel.vue:标题/菜单/更多跳转 UI
  • src/assets/data/*.json:demo 的 routes/layout/menus 数据
  • src/utils/random.jsuuid() 等工具