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

@lius1314/visual-dashboard-scaffold

v1.2.3

Published

最终版-可视化大屏脚手架 - 支持拖拽布局、区块注册、主题配置的 React 可视化大屏组件库

Readme

@lius1314/visual-dashboard-scaffold

React 可视化大屏脚手架。内置拖拽编排、区块注册、多页面路由、主题资源、配置导入导出和运行时持久化,适合把业务图表、3D 地图、指标卡等组件快速装配成可交付的大屏系统。

npm version license

特性

  • 可视化编排:编辑模式下支持图表区块拖拽、缩放、复制、删除、层级调整和独立样式配置。
  • 像素布局:中间区域使用像素坐标布局,适合大屏设计稿落地;默认 1920 x 1080,并支持常见宽高比预设。
  • 区块注册:通过 registerBlock 把任意 React 组件绑定到区块 ID,SDK 不绑定具体图表库。
  • 多页面路由:内置 HashRouter,使用 /#/page/:pageId 渲染页面,支持页面增删改、复制、排序和参数传递。
  • 全局头尾区域headerfooter 为全局共享配置;每个页面独立维护 middlechartBlocks
  • 主题资源:内置多套布局主题、背景图、卡片图、顶部/底部装饰和 Font Awesome 图标资源。
  • 自适应适配:预览模式下接入 autofit.js 做等比缩放;编辑模式下使用独立缩放,方便调参。
  • 配置持久化:Zustand + 混合存储,元信息写入 localStorage,页面详情写入 IndexedDB,并对高频拖拽写入做节流。
  • 导入导出:支持导出纯 JSON 配置;initialConfig 可作为首次启动默认配置,设置面板可一键重置。
  • 事件与行为:支持区块、头部面板、底部按钮配置跳转页面、发送事件、自定义 handler 或无操作。
  • 出场动画:支持全局卡片出场动画和单区块动画覆盖。

安装

npm install @lius1314/visual-dashboard-scaffold

宿主项目需要提供以下 peer dependencies:

npm install react react-dom react-router-dom

兼容要求:

| 依赖 | 要求 | | --- | --- | | react | >=18.0.0 | | react-dom | >=18.0.0 | | react-router-dom | >=6.0.0 |

当前项目 demo 使用 React 19 和 react-router-dom 7,库本身按 peer dependency 兼容 React 18+。

静态资源

SDK 运行时会通过 /themes/images/fonts/css/webfonts 等路径加载内置主题、图片、字体和图标资源。使用 npm 包时,需要把包内 public/ 目录同步到宿主项目静态资源目录。

node_modules/@lius1314/visual-dashboard-scaffold/public/
  themes/     -> 宿主项目 public/themes/
  images/     -> 宿主项目 public/images/
  fonts/      -> 宿主项目 public/fonts/
  css/        -> 宿主项目 public/css/
  webfonts/   -> 宿主项目 public/webfonts/

推荐在宿主项目 package.json 中加入:

{
  "scripts": {
    "sync:datavis-assets": "node -e \"require('fs').cpSync('node_modules/@lius1314/visual-dashboard-scaffold/public', 'public', { recursive: true, force: false })\"",
    "postinstall": "npm run sync:datavis-assets"
  }
}

或在 Vite 中使用 vite-plugin-static-copy

import { defineConfig } from 'vite';
import { viteStaticCopy } from 'vite-plugin-static-copy';

export default defineConfig({
  plugins: [
    viteStaticCopy({
      targets: [
        {
          src: 'node_modules/@lius1314/visual-dashboard-scaffold/public/*',
          dest: '.',
        },
      ],
    }),
  ],
});

重置内置资源

如果宿主项目里的主题、背景、卡片、字体或图标资源被改乱,或升级 SDK 后希望恢复到当前包内置版本,可以重置 SDK 相关静态目录:

{
  "scripts": {
    "reset:datavis-assets": "node -e \"const fs=require('fs'); ['themes','images','fonts','css','webfonts'].forEach(d=>fs.rmSync('public/'+d,{recursive:true,force:true})); fs.cpSync('node_modules/@lius1314/visual-dashboard-scaffold/public','public',{recursive:true,force:true});\""
  }
}

执行:

npm run reset:datavis-assets

这个命令只会重置 public/themespublic/imagespublic/fontspublic/csspublic/webfonts 这些 SDK 使用的目录。若你把业务自有图片也放在这些目录里,执行前请先备份,或把自有素材放到独立目录并通过 configureImageAssets({ basePath, assets }) 接入。

如需使用自有图片资源,可在应用入口配置图片清单:

import { configureImageAssets } from '@lius1314/visual-dashboard-scaffold';

configureImageAssets({
  assets: {
    bg: ['city.png', 'factory.png'],
    custom: ['warning-card.png', 'map-panel.png'],
  },
  folders: [
    { key: 'bg', label: '背景' },
    { key: 'custom', label: '业务资源', hint: '项目专用图片' },
  ],
  basePath: '/images',
});

快速开始

完整大屏

import { App } from '@lius1314/visual-dashboard-scaffold';
import '@lius1314/visual-dashboard-scaffold/style.css';

export default function Dashboard() {
  return <App />;
}

只读预览:

<App editable={false} />

允许编辑,但默认进入预览模式:

<App editable defaultEditMode={false} />

传入导出的默认配置:

import defaultConfig from './dashboard-config.json';

<App initialConfig={defaultConfig} />;

initialConfig 仅在首次启动且没有持久化数据时自动加载。后续用户编辑会保存在本地;在设置面板点击“重置”可回到该默认配置。

注册自定义区块

import {
  App,
  registerBlock,
  type BlockComponentProps,
} from '@lius1314/visual-dashboard-scaffold';
import '@lius1314/visual-dashboard-scaffold/style.css';

function SalesLineChart({ title, pageParams, emit }: BlockComponentProps) {
  return (
    <div style={{ width: '100%', height: '100%' }}>
      <button onClick={() => emit?.('sales:refresh', { region: pageParams?.region })}>
        {title}
      </button>
    </div>
  );
}

registerBlock('sales-line-chart', SalesLineChart);

export default function Dashboard() {
  return <App />;
}

区块 ID 与配置中的 chartBlock.layout.i 匹配;未注册时会显示占位内容。内置 demo 区块会在 App 加载时自动注册。

按需组合组件

import {
  OuterContainer,
  HeaderSection,
  MiddleSection,
  FooterSection,
  useDashboardStore,
} from '@lius1314/visual-dashboard-scaffold';
import '@lius1314/visual-dashboard-scaffold/style.css';

export function CustomDashboardShell() {
  const store = useDashboardStore();
  const page = store.pages.find(p => p.id === store.activePageId) ?? store.pages[0];

  return (
    <OuterContainer config={{
      editMode: store.editMode,
      globalBackground: store.globalBackground,
      globalBackgroundSize: store.globalBackgroundSize,
      outer: store.outer,
      header: store.header,
      middle: page.middle,
      footer: store.footer,
      chartBlocks: page.chartBlocks,
    }}>
      <HeaderSection
        config={store.header}
        editMode={store.editMode}
        onUpdateTitle={store.updateHeaderTitle}
        onAddPanel={store.addHeaderPanel}
        onDuplicatePanel={store.duplicateHeaderPanel}
        onRemovePanel={store.removeHeaderPanel}
        onUpdatePanel={store.updateHeaderPanel}
      />
      <MiddleSection
        config={page.middle}
        chartBlocks={page.chartBlocks}
        editMode={store.editMode}
        containerWidth={Number(store.outer.width) || 1920}
        pageId={page.id}
        pageParams={page.params}
        cols={store.cols ?? 24}
        rowHeight={store.rowHeight ?? 60}
        onAddBlock={() => store.addChartBlock(page.id)}
        onDuplicateBlock={(id) => store.duplicateChartBlock(page.id, id)}
        onRemoveBlock={(id) => store.removeChartBlock(page.id, id)}
        onUpdateBlock={(id, patch) => store.updateChartBlock(page.id, id, patch)}
        onLayoutChange={(layouts) => store.updateChartLayouts(page.id, layouts)}
        onReorderBlocks={(ids) => store.reorderChartBlocks(page.id, ids)}
      />
      <FooterSection
        config={store.footer}
        editMode={store.editMode}
        onAddItem={store.addBottomItem}
        onDuplicateItem={store.duplicateBottomItem}
        onRemoveItem={store.removeBottomItem}
        onUpdateItem={store.updateBottomItem}
      />
    </OuterContainer>
  );
}

App Props

| Prop | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | editable | boolean | true | 是否允许编辑。为 false 时隐藏编辑开关、配置面板、图层面板和新增菜单。 | | defaultEditMode | boolean | true | 初始是否处于编辑模式。 | | initialConfig | DashboardConfig \| string | - | 默认配置对象或 JSON 字符串。首次无本地持久化数据时加载。 |

配置模型

当前版本的核心结构如下:

DashboardConfig
  editMode
  globalBackground / globalBackgroundSize
  outer              # 全局画布尺寸、背景、自适应配置
  header             # 全局头部,所有页面共享
  footer             # 全局底部 Dock,所有页面共享
  activePageId
  pages[]
    id / name
    middle           # 页面级中间区域
    chartBlocks[]    # 页面级区块
    params           # 页面参数
    paramDefs        # 参数声明

旧版配置中的页面级 header / footer 仍会在导入和持久化水合时兼容迁移,但新配置应优先使用顶层 headerfooter

多页面与参数

内置路由:

| 路由 | 说明 | | --- | --- | | /#/ | 自动跳转到第一个页面 | | /#/page/:pageId | 渲染指定页面 | | 其他路径 | 自动 fallback 到第一个页面 |

页面参数合并优先级:

URL search params > switchPage params > PageConfig.params

在区块中跳转并传参:

import { usePageContext } from '@lius1314/visual-dashboard-scaffold';

function RegionCard() {
  const { switchPage, pageParams } = usePageContext();

  return (
    <button onClick={() => switchPage('detail-page-id', { region: pageParams.region ?? 'beijing' })}>
      查看详情
    </button>
  );
}

通过 store 更新持久化页面参数:

const updatePageParams = useDashboardStore(s => s.updatePageParams);

updatePageParams(pageId, {
  region: 'beijing',
  refreshInterval: 30,
});

也可以直接分享带参数的链接:

/#/page/detail-page-id?region=beijing&theme=dark

行为配置

底部按钮、头部面板和图表区块都支持 action

type BlockAction =
  | { type: 'switchPage'; pageId: string; params?: Record<string, unknown> }
  | { type: 'emitEvent'; eventName: string; payload?: unknown }
  | { type: 'custom'; handlerKey: string }
  | { type: 'none' };

注册自定义 handler:

import { registerActionHandler } from '@lius1314/visual-dashboard-scaffold';

const unsubscribe = registerActionHandler('openAlarmDialog', () => {
  console.log('open alarm dialog');
});

事件通信

区块组件可通过 props 中的 emit 发送页面级事件,也可以直接使用 eventBususeEventBus

import { useEventBus, type BlockComponentProps } from '@lius1314/visual-dashboard-scaffold';

function Sender({ emit }: BlockComponentProps) {
  return <button onClick={() => emit?.('map:clicked', { city: '杭州' })}>发送</button>;
}

function Receiver({ pageId }: BlockComponentProps) {
  useEventBus('map:clicked', (payload) => {
    console.log(payload);
  }, { scope: pageId ? `page:${pageId}` : undefined });

  return <div>监听地图点击</div>;
}

页面卸载时,SDK 会清理 page:${pageId} scope 下的监听,避免跨页面误触发。

Store API

import { useDashboardStore } from '@lius1314/visual-dashboard-scaffold';

const store = useDashboardStore.getState();

常用 action:

| 分类 | API | | --- | --- | | 页面 | switchPageaddPageremovePagerenamePagereorderPagesupdatePageParams | | 全局 | setEditModesetGlobalBackgroundupdateOuterscaleResolution | | 头部 | updateHeaderupdateHeaderTitleaddHeaderPanelduplicateHeaderPanelremoveHeaderPanelupdateHeaderPanelreorderHeaderPanels | | 中间区域 | updateMiddle | | 底部 | updateFooteraddBottomItemduplicateBottomItemremoveBottomItemupdateBottomItem | | 区块 | addChartBlockduplicateChartBlockremoveChartBlockupdateChartBlockupdateChartLayoutsreorderChartBlocks | | 配置 | exportConfigimportConfigresetConfigsetConfigapplyLayoutTheme |

设计尺寸

设置面板提供常见宽高比预设:

  • 16:9 标准宽屏
  • 16:10 办公宽屏
  • 21:9 超宽屏
  • 32:9 拼接屏
  • 9:16 竖屏

项目会把输入比例归一化为适合编辑的设计尺寸,并同步 outer.widthouter.heightautofit.designWidthautofit.designHeight。旧配置导入时,如果区块坐标超出新设计尺寸,会按比例迁移布局。

本地开发

npm install
npm run dev
npm run build
npm run build:lib

构建脚本:

| 命令 | 说明 | | --- | --- | | npm run dev | 启动 Vite demo | | npm run build | 构建 demo 并生成类型 | | npm run build:lib | 以库模式构建 npm 包产物 | | npm run build:types | 仅生成类型声明 | | npm run preview | 预览构建结果 |

导出内容

主要导出:

  • 主组件:App
  • 布局组件:OuterContainerHeaderSectionMiddleSectionFooterSectionSettingsPanelChartBlock
  • Store:useDashboardStoredefaultDashboardConfigdefaultConfiguseConfigStore
  • 区块:registerBlockgetBlockComponentgetRegisteredBlockIds
  • 事件:eventBususeEventBus
  • 页面:PageContextusePageContextusePageParams
  • 资源:configureImageAssetsgetImageAssetsConfiggetFolderMetaListgetImagesInFoldertoImagePath
  • 工具:resolveBackgroundresolveBackgroundPropscn
  • 类型:DashboardConfigPageConfigChartBlockConfigBottomSectionConfigHeaderSectionConfigBlockAction

常见问题

为什么主题或图片 404?

通常是没有把包内 public/ 同步到宿主项目静态资源目录。请确认 /themes/theme1.json/images/bg/bg.png/css/all.min.css 等路径能被浏览器直接访问。

为什么 initialConfig 没有覆盖本地编辑结果?

initialConfig 只在首次无持久化数据时加载。已有本地数据时会优先恢复用户编辑结果。需要回到默认配置时,在设置面板点击“重置”,或调用 resetConfig()

如何接入 ECharts、Three.js 或业务组件?

把它们封装成 React 组件,通过 registerBlock('your-block-id', Component) 注册即可。区块容器会传入 widthheightpageIdpageParamsemitswitchPage 等上下文。

如何做跨页面导航高亮?

底部 item 的 action.typeswitchPagepageId 等于当前页时,会进入激活态。可通过 activeIconactiveColoractiveBackground 等字段定制激活样式。

免责声明

本项目内置的图片资源(背景图、卡片素材、头部标题装饰、图标等)及字体文件(优设标题黑、阿里巴巴普惠体、DS-Digital、庞门正道标题体、思源黑体等)均来源于网络,仅供学习和参考使用,不得用于商业用途。如有侵犯您的版权,请联系作者进行删除。