做可视化大屏时,很多项目一开始只是"画几个图表",后面很快会出现一整套工程问题:
- 设计稿怎么适配不同屏幕?
- 图表位置怎么让业务方自己调?
- 多个页面怎么切换和传参?
- 主题、背景、卡片装饰怎么复用?
- 设计好的配置怎么导出、复用、回滚?
- 图表、3D 地图、业务组件之间怎么通信?
@lius1314/visual-dashboard-scaffold 就是围绕这些问题沉淀出来的 React 可视化大屏脚手架。它不试图替代 ECharts、Three.js 或业务组件,而是提供一个可编辑、可配置、可发布的"大屏装配层"。
npm 地址 :www.npmjs.com/package/@li...
当前版本 :v1.2.2
技术栈:React + TypeScript + Zustand + react-grid-layout + react-router-dom + autofit.js

它解决什么问题

传统大屏项目常见做法是:开发先按设计稿写死布局,需求变化时再改代码。这样前期很快,后期很重:
- 图表换位置要改 CSS;
- 卡片尺寸变化要重新算布局;
- 背景图、标题图、底部导航样式散落在代码里;
- 页面跳转和参数传递很容易越写越乱;
- 交付后业务方想微调,只能继续找开发。
这个脚手架把"大屏的框架能力"和"业务组件"拆开:
- 框架负责画布、头部、底部、拖拽、缩放、主题、配置、路由、事件;
- 业务方只需要把图表、地图、指标卡封装成 React 组件,然后注册到指定区块。
最终效果是:开发者可以快速装配页面,业务同学也可以在编辑模式里调整布局和样式,再导出 JSON 配置用于生产环境。
核心能力
1. 可视化编辑
编辑模式下,图表区块可以直接拖拽、缩放、复制、删除和调整层级。区块标题、背景、内容区、卡片背景、标题字体、边距、动画等都可以单独配置。

预览模式下,这些编辑辅助线、拖拽手柄和工具面板会自动隐藏,页面只保留最终展示效果。
2. 多页面大屏

SDK 内置 HashRouter,路由格式为:
text
/#/page/:pageId
每个页面独立维护:
- 中间区域配置
middle - 图表区块
chartBlocks - 页面参数
params
头部 header 和底部 footer 是全局共享配置,适合真实大屏系统里"统一标题栏 + 统一底部导航"的场景。
3. 区块注册机制
脚手架不绑定任何图表库。你可以注册 ECharts、AntV、Three.js、地图组件,甚至是一个普通业务表格。
tsx
import {
App,
registerBlock,
type BlockComponentProps,
} from '@lius1314/visual-dashboard-scaffold';
import '@lius1314/visual-dashboard-scaffold/style.css';
function SalesChart({ title, pageParams, emit }: BlockComponentProps) {
return (
<div style={{ width: '100%', height: '100%' }}>
<button onClick={() => emit?.('sales:refresh', { region: pageParams?.region })}>
{title}
</button>
</div>
);
}
registerBlock('sales-chart', SalesChart);
export default function AppPage() {
return <App />;
}
配置中的 chartBlock.layout.i 与注册 ID 对上后,SDK 就会在对应区块里渲染你的组件。
4. 主题和素材资源


项目内置了多类静态资源:
themes/:布局主题 JSONimages/bg/:全局背景images/card/:卡片和标题装饰images/header/:顶部装饰images/footer/:底部装饰fonts/:常用大屏字体css/与webfonts/:Font Awesome 图标资源
资源选择器会按分类展示,便于在编辑面板里直接切换背景、卡片、顶部或底部样式。
如果项目有自己的素材,也可以运行时注入:
ts
import { configureImageAssets } from '@lius1314/visual-dashboard-scaffold';
configureImageAssets({
assets: {
bg: ['factory.png', 'city.png'],
business: ['alarm-card.png', 'device-panel.png'],
},
folders: [
{ key: 'bg', label: '背景' },
{ key: 'business', label: '业务素材' },
],
basePath: '/images',
});
5. 配置导入导出

编辑完成后,可以在设置面板导出 JSON 配置。宿主项目下次启动时把 JSON 作为 initialConfig 传入即可:
tsx
import { App } from '@lius1314/visual-dashboard-scaffold';
import config from './dashboard-config.json';
export default function Dashboard() {
return <App initialConfig={config} defaultEditMode={false} />;
}
initialConfig 的设计比较克制:它只在首次没有本地持久化数据时加载。用户后续编辑会被保存在本地,不会被每次刷新覆盖。如果需要回到默认配置,可以点击设置面板里的"重置"。
6. 本地持久化
状态管理使用 Zustand。持久化没有简单地把整个大 JSON 都塞进 localStorage,而是使用混合存储:
- 小体积元信息写入
localStorage - 页面详情写入 IndexedDB
- 高频拖拽更新做节流合并
- 页面删除后清理 IndexedDB 中的旧数据
这样在拖拽、缩放、频繁调整区块时,不会因为不断 JSON.stringify 大配置而明显卡顿。
7. 页面跳转和参数传递
页面参数支持三层合并:

text
URL search params > switchPage params > PageConfig.params
区块内部可以通过 usePageContext 获取页面上下文:
tsx
import { usePageContext } from '@lius1314/visual-dashboard-scaffold';
function RegionCard() {
const { switchPage, pageParams } = usePageContext();
return (
<button onClick={() => switchPage('detail-page', { region: pageParams.region ?? 'beijing' })}>
查看详情
</button>
);
}
也可以直接访问可分享链接:
text
/#/page/detail-page?region=beijing&theme=dark
8. 行为系统
底部按钮、头部面板、图表区块都可以配置点击行为:

ts
type BlockAction =
| { type: 'switchPage'; pageId: string; params?: Record<string, unknown> }
| { type: 'emitEvent'; eventName: string; payload?: unknown }
| { type: 'custom'; handlerKey: string }
| { type: 'none' };
常见场景:
- 点击底部按钮跳转页面;
- 点击地图区块发送事件;
- 点击头部指标面板打开业务弹窗;
- 只展示不响应点击。
自定义 handler 也可以在宿主项目注册:
tsx
import { registerActionHandler } from '@lius1314/visual-dashboard-scaffold';
registerActionHandler('openAlarmDialog', () => {
console.log('打开告警弹窗');
});
架构拆解
整体结构可以理解成三层:
text
OuterContainer
HeaderSection # 全局头部:标题 + 可拖拽面板
MiddleSection # 页面主体:图表区块编排
FooterSection # 全局底部:导航 Dock
对应的数据结构:
text
DashboardConfig
outer
header
footer
activePageId
pages[]
middle
chartBlocks[]
params
这个结构的好处是边界非常清楚:
- 全局区域统一配置;
- 页面内容相互隔离;
- 区块组件独立注册;
- 配置可以完整序列化;
- 宿主项目仍然掌握业务组件的实现。
拖拽布局为什么选择像素坐标

很多后台系统更适合网格栅格,但可视化大屏通常来自设计稿,落点是明确的像素位置。如果继续用普通响应式布局,开发时反而要不断换算。
这个项目的 ChartBlockLayout 使用像素坐标:
ts
interface ChartBlockLayout {
i: string;
x: number;
y: number;
w: number;
h: number;
zIndex?: number;
minW?: number;
minH?: number;
}
它更贴近设计交付:区块放在什么位置、占多大、层级多少,都能直接表达。预览时再交给 autofit.js 做整体等比缩放。
自适应方案
预览模式下,SDK 使用 autofit.js 按设计尺寸等比缩放。默认设计尺寸是 1920 x 1080,设置面板支持:

16:9标准宽屏16:10办公宽屏21:9超宽屏32:9拼接屏9:16竖屏
编辑模式下,不直接把整个 body 交给 autofit 缩放,而是对内容区域做单独缩放。这样工具栏、抽屉、配置面板仍然保持可操作,不会出现编辑 UI 被一起缩小后难以点击的问题。
事件通信
脚手架内置 EventBus,支持页面级 scope。发送方可以这样写:
tsx
function MapBlock({ emit }: BlockComponentProps) {
return (
<button onClick={() => emit?.('map:regionClicked', { region: '华东' })}>
点击区域
</button>
);
}
接收方监听同一页面内的事件:
tsx
import { useEventBus, type BlockComponentProps } from '@lius1314/visual-dashboard-scaffold';
function TrendBlock({ pageId }: BlockComponentProps) {
useEventBus('map:regionClicked', (payload) => {
console.log('刷新趋势图', payload);
}, { scope: pageId ? `page:${pageId}` : undefined });
return <div>趋势图</div>;
}
页面卸载时,SDK 会清理当前页面 scope 下的监听,避免切换页面后旧组件继续响应事件。
快速接入

安装:
bash
npm install @lius1314/visual-dashboard-scaffold
npm install react react-dom react-router-dom
宿主入口:
tsx
import { App } from '@lius1314/visual-dashboard-scaffold';
import '@lius1314/visual-dashboard-scaffold/style.css';
export default function Dashboard() {
return <App editable defaultEditMode={false} />;
}
只读生产模式:
tsx
<App editable={false} initialConfig={config} />
自定义区块:
tsx
import { registerBlock, type BlockComponentProps } from '@lius1314/visual-dashboard-scaffold';
function DevicePanel({ title, pageParams }: BlockComponentProps) {
return (
<div style={{ width: '100%', height: '100%' }}>
{title}: {String(pageParams?.deviceId ?? '-')}
</div>
);
}
registerBlock('device-panel', DevicePanel);
使用时要注意的点
1. 静态资源需要同步
npm 包会携带 public/ 目录,但浏览器最终访问的是宿主项目的静态资源路径。因此需要把包里的 public 同步到宿主 public:
text
node_modules/@lius1314/visual-dashboard-scaffold/public -> public
否则主题、背景、字体、Font Awesome 图标可能出现 404。
如果资源被改乱,或升级 SDK 后想恢复内置资源,可以加一个重置脚本:
json
{
"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});""
}
}
然后执行:
bash
npm run reset:datavis-assets
这个命令只重置 SDK 相关的 themes、images、fonts、css、webfonts 目录。如果业务素材也放在这些目录里,执行前要先备份,或者把业务素材放到独立目录,再用 configureImageAssets 接入。
2. initialConfig 不是强制覆盖
它是"首次默认配置",不是"每次启动覆盖配置"。这样设计是为了保护用户在编辑模式里的本地修改。
如果要强制恢复默认,可以使用设置面板的"重置",或调用 store 的 resetConfig()。
3. header/footer 已经全局化
新版配置里,header 和 footer 位于 DashboardConfig 顶层。旧版页面级 header / footer 仍会兼容迁移,但新项目建议统一使用顶层字段。
适合哪些场景
这个脚手架适合:
- 智慧园区、智慧工厂、智慧城市类大屏;
- 运维监控、态势感知、设备监控系统;
- 需要多页面导航和页面参数传递的大屏;
- 需要交付后可视化调参、导出配置的项目;
- 想把图表组件和大屏框架解耦的团队。
如果只是一个固定的单页展示、没有编辑配置需求,手写页面可能更轻。但只要项目开始出现"可配置、可复用、多页面、可交付"的要求,这类脚手架就能明显减少重复劳动。
总结
@lius1314/visual-dashboard-scaffold 的定位不是图表库,而是大屏应用的装配层。
它把拖拽布局、多页面路由、主题素材、配置持久化、导入导出、事件通信和行为配置这些通用能力沉到框架里,让业务开发更专注于图表、地图和数据逻辑本身。
对于需要快速搭建可视化大屏,又希望后续能持续调整和复用配置的项目,它可以作为一个比较完整的起点。
项目地址:www.npmjs.com/package/@li...
如果你对可视化、3D技术感兴趣的话,欢迎来公众号 柳杉前端 瞧一瞧 看一看