可视化大屏 SDK 数据结构设计:从拖拽组件到最终渲染的全链路

源码

第三方库

react-dnd

react-grid-layout

可视化大屏 SDK 数据结构设计:从拖拽组件到最终渲染的全链路

在可视化编辑器或低代码平台中,数据结构是整个系统的核心。
只有设计合理的 Schema,才能让「从物料库拖拽组件 → 编辑参数 → 渲染成视图」这条链路高效稳定地运行。

本文将结合一个 React + TypeScript + react-grid-layout 的可视化 SDK 项目,详细介绍组件数据结构、布局存储、拖拽行为流转到最终渲染的全链路实现。

  1. 核心数据结构概览

项目中主要有三类数据:
1. 组件物料(Material)
• 描述一个可用的组件(包括内置组件 & 用户上传的 UMD 组件)
• 不直接参与渲染,而是作为可选物料展示在物料库中
2. 布局项(LayoutItem)
• 描述某个组件在编辑区域的位置、尺寸等信息(react-grid-layout 格式)
• 是渲染的直接依据
3. 页面配置(PageConfig)
• 存储一个页面所有组件的配置(包含布局信息 + 组件参数 + 数据源)

1.1 组件物料结构

// src/types/material.ts

export interface MaterialItem {
  id: string;                  // 唯一 ID(物料库用)
  name: string;                // 组件名称
  type: 'builtin' | 'custom';  // 内置 / 自定义
  icon?: string;               // 图标地址
  // 内置组件
  component?: React.ComponentType<any>;
  // 自定义组件
  url?: string;                 // UMD 组件地址
  globalName?: string;          // UMD 全局变量名
  version?: string;             // 版本号
  // 配置 schema(用于生成参数表单)
  configSchema?: Record<string, any>;
}

设计要点
• 内置组件直接引用 React 组件
• 自定义组件仅存储 URL & globalName,由 iframe 沙箱动态加载
• configSchema 用于动态生成编辑表单,避免硬编码

1.2 布局项结构(react-grid-layout)

// src/types/layout.ts

export interface LayoutItem {
  i: string;      // 对应 PageComponent.id
  x: number;
  y: number;
  w: number;
  h: number;
  static?: boolean;
}

这是 react-grid-layout 标准结构,用来描述组件在画布中的位置和大小。

1.3 页面配置结构

// src/types/page.ts

export interface PageComponent {
  id: string;                    // 唯一 ID
  materialId: string;            // 对应 MaterialItem.id
  props?: Record<string, any>;   // 组件配置参数
  dataSource?: {
    type: 'http' | 'code';       // HTTP 接口 / 自定义代码
    url?: string;
    transformCode?: string;      // 数据转换代码
  };
}

export interface PageConfig {
  layout: LayoutItem[];          // 组件位置 & 尺寸
  components: PageComponent[];   // 组件列表
}

  1. 数据流:从拖拽到渲染

整体流程可以分成 4 步:

物料库(MaterialItem)
→ 拖拽生成 PageComponent & LayoutItem
→ 存储到 PageConfig
→ 渲染组件

2.1 拖拽生成布局项

当用户从物料库拖拽组件到编辑区域时:

// 伪代码

function handleDrop(material: MaterialItem, position: {x: number, y: number}) {
  const id = nanoid();
  
  const newComponent: PageComponent = {
    id,
    materialId: material.id,
    props: {}, // 默认参数
  };

  const newLayout: LayoutItem = {
    i: id,
    x: position.x,
    y: position.y,
    w: 4,
    h: 4,
  };

  pageConfig.components.push(newComponent);
  pageConfig.layout.push(newLayout);
}

这里的 layout.i 和 component.id 对应绑定,保证布局与组件一一映射。

2.2 编辑组件参数
• 读取 MaterialItem.configSchema 生成表单
• 表单修改后更新 PageComponent.props
• 数据源配置也存储在 PageComponent.dataSource

// 修改 props
function updateComponentProps(id: string, newProps: Record<string, any>) {
  const comp = pageConfig.components.find(c => c.id === id);
  if (comp) {
    comp.props = { ...comp.props, ...newProps };
  }
}

2.3 渲染组件

渲染时需要:
1. 根据 layout 渲染 GridLayout 容器
2. 根据 component.materialId 找到对应物料(内置 or 自定义)
3. 内置组件直接渲染,UMD 组件用 iframe 沙箱加载

// RenderCanvas.tsx
<GridLayout layout={pageConfig.layout} cols={12} rowHeight={30}>
  {pageConfig.components.map(comp => {
    const material = materialMap[comp.materialId];
    if (material.type === 'builtin') {
      const Component = material.component!;
      return <Component key={comp.id} {...comp.props} />;
    } else {
      return (
        <IframeSandbox
          key={comp.id}
          url={material.url!}
          globalName={material.globalName!}
          props={comp.props}
        />
      );
    }
  })}
</GridLayout>

  1. 设计亮点
    • 统一 ID 绑定:layout 和 component 用同一个 id 映射,更新同步
    • 物料 & 实例分离:物料只描述组件信息,实例化后的配置在 PageConfig
    • 支持多种数据源:接口请求 & 自定义代码都能配置
    • 内置 & 自定义组件共存:内置直接渲染,自定义用沙箱加载

  1. 总结

通过这套 Schema:
• 拖拽、编辑、渲染有了统一的结构
• 内置 & UMD 自定义组件都能流畅接入
• 数据源配置灵活,支持接口与自定义代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天也想MK代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值