【Formily】实现antd designable平台的简易版本

1 背景

designable设计器平台,是阿里开发的一个表单设计器,基于formily表单引擎、schema json实现的。designable源码在这里

这里的功能,其实也类似于第代码平台,可以查看低代码引擎
非官方文档:designable工程结构

现在,由于业务需求,我们需要实现designable平台的一个简易版的表单设计器,包括组件拖拽功能,对表单字段的schema配置,即右侧的【属性配置】,还需要支持reaction字段的联动。

在这里插入图片描述

拖动组件
放置到放置区域
获取当前组件的类别和schema json
在放置区域渲染当前组件

功能列表:

  • 拖拽区域
    • 渲染组件列表,包括组件的名称和icon
    • 组件可以拖动
  • 放置区域
    • 拖动后的组件可以放置,并且拖拽区域的组件依旧存在
    • 放置区域的组件可以正确渲染相应样式,使用formily的schema json渲染
    • 放置区域的组件可以上下移动排序,同时可以进行删除和编辑操作
  • 字段配置
    • 支持常规的key,title,default、required等字段
    • 对于某些用户可视化的字段,比如title,需要支持多语言(自定义输入和json输入)
    • 支持展示配置后的schema json,也支持修改schema json后,表单组件同步更新渲染
  • 联动逻辑
    • 代码编辑框
    • 代码提示
    • 字段的组装

2 技术栈

antd:react web组件库
formily:表单引擎,可以根据schema json直接渲染表单
react-beautiful-dnd:常用于列表的拖拽,支持排序
react-dnd:拖拽和放置功能,比如上面截图的组件拖拽

3 组件拖拽和放置

3.1 类型定义

右侧组件类型:id唯一标识,scheme存放渲染表单的json文件

export interface ComponentConfig {
   
   
  id?: string; // 唯一标识,随机生成,且不可更改
  key: string; // 表单字段key,用户可以更改
  title: string; // 拖拽区域的文案,不可更改
  component_type: ComponentType; // RN侧的组件标识,不可更改
  schema: ISchema;
}

export enum ComponentType {
   
   
  TextInputRow = 'TextInputRow', // 文本输入框
  DateInputRow = 'DateInputRow', // 时间选择器
  CheckBox = 'CheckBox',
}

右侧组件列表:

export const ComponentConfigs: ComponentConfig[] = [
  {
   
   
    key: ComponentType.TextInputRow,
    schema: {
   
   
      type: 'string',
      'x-component': 'Input',
      'x-decorator': 'FormItem',
    },
  },
  {
   
   
    key: ComponentType.DateInputRow,
    schema: {
   
   
      type: 'string',
      'x-component': 'DatePicker',
      'x-decorator': 'FormItem',
    },
  },
  {
   
   
    key: ComponentType.CheckBox,
    schema: {
   
   
      type: 'string',
      'x-component': 'Checkbox',
      'x-decorator': 'FormItem',
    },
  },
].map((i) => ({
   
    ...i, title: i.key, component_type: i.key }));

3.2 拖拽

useDrag:让DOM实现拖拽能力的构子

  • 请求参数:
    • type: 指定元素的类型,只有 类型相同的元素 才能进行drop操作
    • item: 元素在拖拽过程中,描述该对象的数据。可以在useDrop中的drop接收到该数据
    • collect: 返回一个描述状态的普通对象,然后返回以注入到组件中。它接收两个参数,一个DragTargetMonitor实例和拖拽元素描述信息item
  • 返回参数:
    • 第一个返回值:是一个对象 表示关联在拖拽过程中的变量,需要在传入useDrag的规范方法的collect属性中进行映射绑定, 比如:isDraging, canDrag
    • 第二个返回值: 代表拖拽元素的ref
    • 第三个返回值: 代表拖拽元素拖拽后实际操作到的dom
// 用于包裹每一个可以拖拽的组件
export const WrapComponent = (props: DndComponentDndItem) => {
   
   
  const [, drag] = useDrag(() => ({
   
   
    type: ItemTypes.CARD,
    item: props.config,
    // collect中可以监控drag状态变更,并把状态暴露给组件
    collect: (monitor) => ({
   
    isDragging: !!monitor.isDragging() }),
  }));
  return (
    <div
      style={
   
   {
   
   
        width: 100,
        cursor: 'move',
        height: 50,
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: 'white',
        borderRadius: 4,
      }}
      ref={
   
   drag}
    >
      {
   
   props.children}
    </div>
  );
};

3.3 放置

useDrop:让拖拽物放置的构子

  • 请求参数:
    • type: 指定元素的类型,只有 类型相同的元素 才能进行drop操作
    • item: 元素在拖拽过程中,描述该对象的数据。可以在useDrop中的drop接收到该数据
    • collect: 返回一个描述状态的普通对象,然后返回以注入到组件中。它接收两个参数,一个DragTargetMonitor实例和拖拽元素描述信息item
  • 返回参数:
    • 第一个返回值:是一个对象 表示关联在拖拽过程中的变量,需要在传入useDrag的规范方法的collect属性中进行映射绑定, 比如:isDraging, canDrag
    • 第二个返回值: 代表拖拽元素的ref
    • 第三个返回值: 代表拖拽元素拖拽后实际操作到的dom
export const DropContainer = observer((props: DndComponentDndItem) => {
   
   
  const [droppedItems, setDroppedItems] = useState<FieldConfigItem[]>([]);
  const field 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值