Designable和Formily实现简单的低代码平台学习

引言

低代码平台是一种允许用户通过图形化界面而非传统编程方式来创建应用程序的开发方法。它大大降低了应用开发的门槛,使非专业开发人员也能快速构建功能完善的应用程序。本文将介绍如何使用Designable和Formily这两个强大的开源工具,快速搭建一个简单的低代码平台,特别适合表单类应用的开发。

Designable和Formily简介

Designable

Designable是一个基于React的UI设计器组件库,它提供了拖拽式的界面设计能力,让用户可以通过简单的拖拽操作来构建复杂的用户界面。Designable的核心特性包括:

  • 可视化拖拽设计
  • 组件属性面板
  • 实时预览
  • JSON Schema支持

Formily

Formily是一个强大的表单解决方案,它提供了表单渲染、数据收集、验证等功能。Formily的核心特性包括:

  • 高性能表单渲染
  • 丰富的表单组件
  • 强大的数据验证
  • 与JSON Schema无缝集成

环境准备

在开始之前,我们需要准备以下环境:

  1. Node.js (推荐版本 >= 14.0.0)
  2. 一个代码编辑器,如VS Code
  3. 基本的React知识

首先,我们需要创建一个新的React项目:

npx create-react-app lowcode-platform
cd lowcode-platform

接下来,安装Designable和Formily的相关依赖:

npm install @designable/core @designable/react-formily @formily/antd @formily/core @formily/react

基础实现步骤

1. 创建设计器组件

首先,我们需要创建一个设计器组件,让用户可以通过拖拽方式设计表单:

// src/Designer.jsx
import React from 'react';
import { Designable } from '@designable/core';
import { DesignableProvider } from '@designable/react-formily';
import { FormProvider } from '@formily/react';
import { Form as AntdForm } from 'antd';
import { createSchemaField } from '@formily/react';

const SchemaField = createSchemaField({
  components: {
    Input: Input,
    Select: Select,
    DatePicker: DatePicker,
    // 添加更多需要的组件
  },
});

const Designer = () => {
  return (
    <DesignableProvider>
      <Designable
        components={{
          Form: AntdForm,
          SchemaField,
        }}
        onMount={(designer) => {
          // 初始化设计器
        }}
      />
    </DesignableProvider>
  );
};

export default Designer;

2. 创建预览组件

接下来,我们需要创建一个预览组件,用于实时显示设计结果:

// src/Preview.jsx
import React from 'react';
import { FormProvider } from '@formily/react';
import { Form as AntdForm } from 'antd';
import { createSchemaField } from '@formily/react';

const SchemaField = createSchemaField({
  components: {
    Input: Input,
    Select: Select,
    DatePicker: DatePicker,
    // 添加更多需要的组件
  },
});

const Preview = ({ schema }) => {
  return (
    <FormProvider>
      <AntdForm>
        <SchemaField schema={schema} />
      </AntdForm>
    </FormProvider>
  );
};

export default Preview;

3. 创建主应用组件

最后,我们需要创建一个主应用组件,将设计器和预览组件组合在一起:

// src/App.jsx
import React, { useState } from 'react';
import Designer from './Designer';
import Preview from './Preview';

const App = () => {
  const [schema, setSchema] = useState(null);

  return (
    <div style={{ display: 'flex', height: '100vh' }}>
      <div style={{ flex: 1, borderRight: '1px solid #eee' }}>
        <Designer onSchemaChange={setSchema} />
      </div>
      <div style={{ flex: 1 }}>
        <Preview schema={schema} />
      </div>
    </div>
  );
};

export default App;

示例代码

下面是一个更完整的示例,包含基本的表单组件和设计器功能:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createDesigner } from '@designable/core';
import { DesignableProvider } from '@designable/react-formily';
import { FormProvider } from '@formily/react';
import { Form as AntdForm, Input, Select, DatePicker, Button } from 'antd';
import { createSchemaField } from '@formily/react';
import App from './App';

const SchemaField = createSchemaField({
  components: {
    Input,
    Select,
    DatePicker,
    Button,
  },
});

const designer = createDesigner();

ReactDOM.render(
  <React.StrictMode>
    <DesignableProvider designer={designer}>
      <FormProvider>
        <App />
      </FormProvider>
    </DesignableProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
// src/App.jsx
import React, { useState } from 'react';
import Designer from './Designer';
import Preview from './Preview';

const App = () => {
  const [schema, setSchema] = useState(null);

  return (
    <div style={{ display: 'flex', height: '100vh' }}>
      <div style={{ flex: 1, borderRight: '1px solid #eee' }}>
        <Designer onSchemaChange={setSchema} />
      </div>
      <div style={{ flex: 1 }}>
        <Preview schema={schema} />
      </div>
    </div>
  );
};

export default App;
// src/Designer.jsx
import React from 'react';
import { Designable } from '@designable/core';
import { DnFC } from '@designable/react';
import { FormTab } from '@designable/react-components';
import { FormProvider } from '@formily/react';
import { Form as AntdForm } from 'antd';
import { createSchemaField } from '@formily/react';
import { Input, Select, DatePicker, Button } from 'antd';

const SchemaField = createSchemaField({
  components: {
    Input,
    Select,
    DatePicker,
    Button,
  },
});

const Designer: DnFC = (props) => {
  return (
    <DesignableProvider>
      <Designable
        {...props}
        components={{
          Form: AntdForm,
          SchemaField,
        }}
        onMount={(designer) => {
          // 注册组件
          designer.registerComponent('Input', {
            title: '输入框',
            defaultProps: {
              type: 'text',
              placeholder: '请输入',
            },
          });
          
          designer.registerComponent('Select', {
            title: '下拉选择',
            defaultProps: {
              placeholder: '请选择',
            },
          });
          
          designer.registerComponent('DatePicker', {
            title: '日期选择',
            defaultProps: {
              placeholder: '请选择日期',
            },
          });
          
          designer.registerComponent('Button', {
            title: '按钮',
            defaultProps: {
              type: 'primary',
              children: '按钮',
            },
          });
        }}
      />
    </DesignableProvider>
  );
};

export default Designer;
// src/Preview.jsx
import React from 'react';
import { FormProvider } from '@formily/react';
import { Form as AntdForm } from 'antd';
import { createSchemaField } from '@formily/react';
import { Input, Select, DatePicker, Button } from 'antd';

const SchemaField = createSchemaField({
  components: {
    Input,
    Select,
    DatePicker,
    Button,
  },
});

const Preview = ({ schema }) => {
  return (
    <FormProvider>
      <AntdForm layout="vertical">
        {schema && <SchemaField schema={schema} />}
      </AntdForm>
    </FormProvider>
  );
};

export default Preview;

进阶功能

1. 添加组件属性面板

为了增强用户体验,我们可以添加一个属性面板,让用户可以编辑选中组件的属性:

// src/PropertyPanel.jsx
import React from 'react';
import { useDesigner } from '@designable/core';
import { useField } from '@formily/react';
import { Input, Select, DatePicker, InputNumber, Switch } from 'antd';

const PropertyPanel = () => {
  const designer = useDesigner();
  const field = useField();
  
  if (!field || !field.designable) {
    return null;
  }
  
  const componentType = field.componentType;
  const props = field.props || {};
  
  const renderPropertyEditor = () => {
    switch (componentType) {
      case 'Input':
        return (
          <>
            <Input
              value={props.placeholder || ''}
              onChange={(e) => {
                field.props.placeholder = e.target.value;
              }}
              placeholder="占位文本"
            />
            <Input
              value={props.title || ''}
              onChange={(e) => {
                field.title = e.target.value;
              }}
              placeholder="标题"
            />
          </>
        );
      case 'Select':
        return (
          <>
            <Input
              value={props.placeholder || ''}
              onChange={(e) => {
                field.props.placeholder = e.target.value;
              }}
              placeholder="占位文本"
            />
            <Input
              value={props.title || ''}
              onChange={(e) => {
                field.title = e.target.value;
              }}
              placeholder="标题"
            />
          </>
        );
      case 'DatePicker':
        return (
          <>
            <Input
              value={props.placeholder || ''}
              onChange={(e) => {
                field.props.placeholder = e.target.value;
              }}
              placeholder="占位文本"
            />
            <Input
              value={props.title || ''}
              onChange={(e) => {
                field.title = e.target.value;
              }}
              placeholder="标题"
            />
          </>
        );
      case 'Button':
        return (
          <>
            <Input
              value={props.children || ''}
              onChange={(e) => {
                field.props.children = e.target.value;
              }}
              placeholder="按钮文本"
            />
            <Select
              value={props.type || 'primary'}
              onChange={(value) => {
                field.props.type = value;
              }}
              style={{ width: '100%', marginTop: 8 }}
            >
              <Select.Option value="primary">主要按钮</Select.Option>
              <Select.Option value="default">默认按钮</Select.Option>
              <Select.Option value="dashed">虚线按钮</Select.Option>
              <Select.Option value="link">链接按钮</Select.Option>
            </Select>
          </>
        );
      default:
        return null;
    }
  };
  
  return (
    <div style={{ padding: 16 }}>
      <h3>属性设置</h3>
      {renderPropertyEditor()}
    </div>
  );
};

export default PropertyPanel;

2. 添加JSON导入导出功能

为了保存和加载设计结果,我们可以添加JSON导入导出功能:

// src/App.jsx
import React, { useState } from 'react';
import Designer from './Designer';
import Preview from './Preview';
import PropertyPanel from './PropertyPanel';
import { Button, Upload, message } from 'antd';
import { DownloadOutlined } from '@ant-design/icons';

const App = () => {
  const [schema, setSchema] = useState(null);

  const handleExport = () => {
    if (!schema) {
      message.warning('没有可导出的表单');
      return;
    }
    
    const dataStr = JSON.stringify(schema, null, 2);
    const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
    
    const exportFileDefaultName = 'form-schema.json';
    
    const linkElement = document.createElement('a');
    linkElement.setAttribute('href', dataUri);
    linkElement.setAttribute('download', exportFileDefaultName);
    linkElement.click();
  };

  const handleImport = (info) => {
    const file = info.file;
    const reader = new FileReader();
    
    reader.onload = (e) => {
      try {
        const content = JSON.parse(e.target.result);
        setSchema(content);
        message.success('表单导入成功');
      } catch (error) {
        message.error('表单导入失败,请检查文件格式');
      }
    };
    
    reader.readAsText(file);
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
      <div style={{ padding: 16, borderBottom: '1px solid #eee' }}>
        <Button 
          type="primary" 
          icon={<DownloadOutlined />} 
          onClick={handleExport}
          style={{ marginRight: 16 }}
        >
          导出表单
        </Button>
        <Upload
          accept=".json"
          beforeUpload={() => false}
          onChange={handleImport}
          showUploadList={false}
        >
          <Button>导入表单</Button>
        </Upload>
      </div>
      <div style={{ display: 'flex', flex: 1 }}>
        <div style={{ flex: 1, borderRight: '1px solid #eee' }}>
          <Designer onSchemaChange={setSchema} />
        </div>
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
          <div style={{ flex: 1, padding: 16, overflow: 'auto' }}>
            <Preview schema={schema} />
          </div>
          <div style={{ height: 300, borderTop: '1px solid #eee' }}>
            <PropertyPanel />
          </div>
        </div>
      </div>
    </div>
  );
};

export default App;

总结

通过本文的介绍,我们学习了如何使用Designable和Formily构建一个简单的低代码平台。我们了解了:

  1. Designable提供了拖拽式设计能力,让用户可以直观地构建表单界面
  2. Formily提供了强大的表单渲染和数据管理能力
  3. 如何将两者结合,创建一个功能完整的低代码表单设计器
  4. 如何添加属性面板、导入导出等高级功能

当然,这只是一个基础的实现,你可以根据实际需求扩展更多功能,比如:

  • 添加更多表单组件
  • 实现表单验证规则设计
  • 添加布局控制功能
  • 集成后端API,实现表单数据的保存和加载
  • 添加主题定制功能

希望这篇文章能帮助你快速入门Designable和Formily,并构建出功能强大的低代码平台!觉得有用就点个关注!我会持续分享更多实用工具和效率技巧。想直接体验?各类免费、开箱即用的在线工具,都在我的【星点工具箱】网站等你来玩!

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐