@lius1314/visual-dashboard-scaffold
v1.2.3
Published
最终版-可视化大屏脚手架 - 支持拖拽布局、区块注册、主题配置的 React 可视化大屏组件库
Maintainers
Readme
@lius1314/visual-dashboard-scaffold
React 可视化大屏脚手架。内置拖拽编排、区块注册、多页面路由、主题资源、配置导入导出和运行时持久化,适合把业务图表、3D 地图、指标卡等组件快速装配成可交付的大屏系统。
特性
- 可视化编排:编辑模式下支持图表区块拖拽、缩放、复制、删除、层级调整和独立样式配置。
- 像素布局:中间区域使用像素坐标布局,适合大屏设计稿落地;默认 1920 x 1080,并支持常见宽高比预设。
- 区块注册:通过
registerBlock把任意 React 组件绑定到区块 ID,SDK 不绑定具体图表库。 - 多页面路由:内置
HashRouter,使用/#/page/:pageId渲染页面,支持页面增删改、复制、排序和参数传递。 - 全局头尾区域:
header和footer为全局共享配置;每个页面独立维护middle与chartBlocks。 - 主题资源:内置多套布局主题、背景图、卡片图、顶部/底部装饰和 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/themes、public/images、public/fonts、public/css、public/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 仍会在导入和持久化水合时兼容迁移,但新配置应优先使用顶层 header 与 footer。
多页面与参数
内置路由:
| 路由 | 说明 |
| --- | --- |
| /#/ | 自动跳转到第一个页面 |
| /#/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 发送页面级事件,也可以直接使用 eventBus 或 useEventBus。
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 |
| --- | --- |
| 页面 | switchPage、addPage、removePage、renamePage、reorderPages、updatePageParams |
| 全局 | setEditMode、setGlobalBackground、updateOuter、scaleResolution |
| 头部 | updateHeader、updateHeaderTitle、addHeaderPanel、duplicateHeaderPanel、removeHeaderPanel、updateHeaderPanel、reorderHeaderPanels |
| 中间区域 | updateMiddle |
| 底部 | updateFooter、addBottomItem、duplicateBottomItem、removeBottomItem、updateBottomItem |
| 区块 | addChartBlock、duplicateChartBlock、removeChartBlock、updateChartBlock、updateChartLayouts、reorderChartBlocks |
| 配置 | exportConfig、importConfig、resetConfig、setConfig、applyLayoutTheme |
设计尺寸
设置面板提供常见宽高比预设:
16:9标准宽屏16:10办公宽屏21:9超宽屏32:9拼接屏9:16竖屏
项目会把输入比例归一化为适合编辑的设计尺寸,并同步 outer.width、outer.height、autofit.designWidth、autofit.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 - 布局组件:
OuterContainer、HeaderSection、MiddleSection、FooterSection、SettingsPanel、ChartBlock - Store:
useDashboardStore、defaultDashboardConfig、defaultConfig、useConfigStore - 区块:
registerBlock、getBlockComponent、getRegisteredBlockIds - 事件:
eventBus、useEventBus - 页面:
PageContext、usePageContext、usePageParams - 资源:
configureImageAssets、getImageAssetsConfig、getFolderMetaList、getImagesInFolder、toImagePath - 工具:
resolveBackground、resolveBackgroundProps、cn - 类型:
DashboardConfig、PageConfig、ChartBlockConfig、BottomSectionConfig、HeaderSectionConfig、BlockAction等
常见问题
为什么主题或图片 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) 注册即可。区块容器会传入 width、height、pageId、pageParams、emit、switchPage 等上下文。
如何做跨页面导航高亮?
底部 item 的 action.type 为 switchPage 且 pageId 等于当前页时,会进入激活态。可通过 activeIcon、activeColor、activeBackground 等字段定制激活样式。
免责声明
本项目内置的图片资源(背景图、卡片素材、头部标题装饰、图标等)及字体文件(优设标题黑、阿里巴巴普惠体、DS-Digital、庞门正道标题体、思源黑体等)均来源于网络,仅供学习和参考使用,不得用于商业用途。如有侵犯您的版权,请联系作者进行删除。
