如何从0到1搭建基于antd的monorepo库——实现JsonSchemaForm组件(三)

文章系列

上一章:# 如何从0到1搭建基于antd的monorepo库------子包初始化(二)

作者有话说

目前已经实现了一部分功能,源代码在 github,欢迎大家 Star 和 PR,一些待实现的功能都在 issue 中,感兴趣的同学可以一起加入进来。

看完这个系列可以收获什么:

  1. 如何使用 pnpm workspace + lerna 搭建 monorepo 仓库
  2. antd 的单个组件怎么进行文件结构的设计
  3. 基于 antd form 实现一个 Json 渲染表单
  4. antd 的打包方式以及如何使用 rollup 实现
  5. 如何发布 monorepo 包到 npm

前瞻

组件库技术选型:

  1. pnpm 10
  2. node 20
  3. lerna 8
  4. react 18
  5. antd 5
  6. dumi 2

正片开始

搭建组件目录结构

进入 packages/basic 子包。

src 下新增 JsonSchemaForm 文件夹,以及 JsonSchemaForm/index.tsJsonSchemaForm/JsonSchemaForm.tsxJsonSchemaForm/JsonSchemaFormItem.tsxJsonSchemaForm/constants.tsJsonSchemaForm/type.ts 文件。

bash 复制代码
- JsonSchemaForm
  - index.ts                # 对外的窗口
  - JsonSchemaForm.tsx      # 组件入口
  - JsonSchemaFormItem.tsx  # 子组件
  - type.ts                 # 类型文件
  - constants.ts            # 常量文件

初始化组件

文章中组件是一步一步设计的,可能会导致代码比较分散,如果想看完整代码可以直接去 github

JsonSchemaForm/JsonSchemaForm.tsx 中新增代码。

tsx 复制代码
import { Button, Form, Space } from 'antd';
import JsonSchemaFormItem from './JsonSchemaFormItem';
import { JsonSchemaFormProps } from './type';

const JsonSchemaForm = ({ schema, formProps }: JsonSchemaFormProps) => {
  const [form] = Form.useForm();

  return (
    <Form scrollToFirstError layout="vertical" {...formProps} form={form}>
      {schema.map((item) => (
        <JsonSchemaFormItem key={item.formItemProps.name} {...item} />
      ))}
      <Form.Item>
        <Space>
          <Button type="primary" htmlType="submit">
            提交
          </Button>
          <Button onClick={() => form.resetFields()}>重置</Button>
        </Space>
      </Form.Item>
    </Form>
  );
};

export default JsonSchemaForm;

JsonSchemaFormItem.tsx 中新增代码。

tsx 复制代码
import { Form } from 'antd';
import React from 'react';
import { RENDER_MAP } from './constants';

const JsonSchemaFormItem = ({ $type, formItemProps, ...componentProps }) => {
  return (
    <Form.Item key={formItemProps.name} {...formItemProps}>
      {React.createElement(RENDER_MAP[$type], {
        ...componentProps,
      })}
    </Form.Item>
  );
};

export default JsonSchemaFormItem;

JsonSchemaForm/constants.ts 中新增代码。

ts 复制代码
import {
  Cascader,
  Checkbox,
  ColorPicker,
  DatePicker,
  Input,
  InputNumber,
  Mentions,
  Radio,
  Rate,
  Segmented,
  Select,
  Slider,
  Switch,
  TimePicker,
  TreeSelect,
  Upload,
} from 'antd';
import type { ComponentType } from 'react';
import JsonSchemaAutoComplete from './components/JsonSchemaAutoComplete';
import { JsonSchemaFormItemTypeType } from './type';

export const RENDER_MAP: Record<
  JsonSchemaFormItemTypeType,
  ComponentType<any>
> = {
  // input
  input: Input,
  password: Input.Password,
  search: Input.Search,
  textarea: Input.TextArea,
  opt: Input.OTP,
  inputNumber: InputNumber,
  mentions: Mentions,
  autoComplete: JsonSchemaAutoComplete,
  // select
  select: Select,
  treeSelect: TreeSelect,
  cascader: Cascader,
  // check
  radio: Radio.Group,
  checkbox: Checkbox.Group,
  segmented: Segmented,
  rate: Rate,
  slider: Slider,
  switch: Switch,
  // picker
  datePicker: DatePicker,
  rangePicker: DatePicker.RangePicker,
  timePicker: TimePicker,
  colorPicker: ColorPicker,
  // upload
  upload: Upload,
  dragger: Upload.Dragger,
};

JsonSchemaForm/type.ts 中新增代码。

ts 复制代码
import {
  AutoCompleteProps,
  CascaderProps,
  ColorPickerProps,
  DatePickerProps,
  DraggerProps,
  FormItemProps,
  FormProps,
  InputNumberProps,
  InputProps,
  MentionsProps,
  RadioGroupProps,
  RateProps,
  SegmentedProps,
  SelectProps,
  SwitchProps,
  TimePickerProps,
  TreeSelectProps,
  UploadProps,
} from 'antd';
import { CheckboxGroupProps } from 'antd/es/checkbox';
import { RangePickerProps } from 'antd/es/date-picker';
import { PasswordProps, SearchProps, TextAreaProps } from 'antd/es/input';
import { OTPProps } from 'antd/es/input/OTP';
import { SliderProps } from 'antd/es/slider';
import { Dispatch, SetStateAction } from 'react';

export type JsonSchemaFormItemTypeType =
  // input
  | 'input'
  | 'password'
  | 'search'
  | 'textarea'
  | 'opt'
  | 'inputNumber'
  | 'mentions'
  | 'autoComplete'
  // select
  | 'select'
  | 'treeSelect'
  | 'cascader'
  // check
  | 'radio'
  | 'slider'
  | 'checkbox'
  | 'segmented'
  | 'switch'
  // picker
  | 'rate'
  | 'datePicker'
  | 'rangePicker'
  | 'timePicker'
  | 'colorPicker'
  // upload
  | 'upload'
  | 'dragger';

export type filterCustomProps<T> = Omit<T, 'onChange'>;

// 基础接口,包含所有表单项共有的属性
interface BaseJsonSchemaFormItem {
  formItemProps: FormItemProps;
  onChange?: (
    value: any,
    [formProps, setFormProps]: [FormProps, Dispatch<SetStateAction<FormProps>>],
  ) => void;
}

interface InputJsonSchemaFormItem
  extends filterCustomProps<InputProps>,
    BaseJsonSchemaFormItem {
  $type: 'input';
}

interface InputPasswordJsonSchemaFormItem
  extends filterCustomProps<PasswordProps>,
    BaseJsonSchemaFormItem {
  $type: 'password';
}

interface InputNumberJsonSchemaFormItem
  extends filterCustomProps<InputNumberProps>,
    BaseJsonSchemaFormItem {
  $type: 'inputNumber';
}

interface TextareaJsonSchemaFormItem
  extends filterCustomProps<TextAreaProps>,
    BaseJsonSchemaFormItem {
  $type: 'textarea';
}

interface SearchJsonSchemaFormItem
  extends filterCustomProps<SearchProps>,
    BaseJsonSchemaFormItem {
  $type: 'search';
}

export interface AutoCompleteJsonSchemaFormItem
  extends Omit<filterCustomProps<AutoCompleteProps>, 'onSearch'>,
    BaseJsonSchemaFormItem {
  $type: 'autoComplete';
  onSearch?: (
    value: string,
    [options, setOptions]: [
      AutoCompleteProps['options'],
      Dispatch<SetStateAction<AutoCompleteProps['options']>>,
    ],
  ) => void;
}

interface SelectJsonSchemaFormItem
  extends filterCustomProps<SelectProps>,
    BaseJsonSchemaFormItem {
  $type: 'select';
}

interface TreeSelectJsonSchemaFormItem
  extends filterCustomProps<TreeSelectProps>,
    BaseJsonSchemaFormItem {
  $type: 'treeSelect';
}

interface OptJsonSchemaFormItem
  extends filterCustomProps<OTPProps>,
    BaseJsonSchemaFormItem {
  $type: 'opt';
}

interface RadioJsonSchemaFormItem
  extends filterCustomProps<RadioGroupProps>,
    BaseJsonSchemaFormItem {
  $type: 'radio';
}

interface CheckboxJsonSchemaFormItem
  extends filterCustomProps<CheckboxGroupProps>,
    BaseJsonSchemaFormItem {
  $type: 'checkbox';
}

interface CascaderJsonSchemaFormItem
  extends filterCustomProps<CascaderProps>,
    BaseJsonSchemaFormItem {
  $type: 'cascader';
}

interface SegmentedJsonSchemaFormItem
  extends filterCustomProps<SegmentedProps>,
    BaseJsonSchemaFormItem {
  $type: 'segmented';
}

interface DatePickerJsonSchemaFormItem
  extends filterCustomProps<DatePickerProps>,
    BaseJsonSchemaFormItem {
  $type: 'datePicker';
}

interface RangePickerJsonSchemaFormItem
  extends filterCustomProps<RangePickerProps>,
    BaseJsonSchemaFormItem {
  $type: 'rangePicker';
}

interface TimePickerJsonSchemaFormItem
  extends filterCustomProps<TimePickerProps>,
    BaseJsonSchemaFormItem {
  $type: 'timePicker';
}

interface ColorPickerJsonSchemaFormItem
  extends filterCustomProps<ColorPickerProps>,
    BaseJsonSchemaFormItem {
  $type: 'colorPicker';
}

interface MentionsJsonSchemaFormItem
  extends filterCustomProps<MentionsProps>,
    BaseJsonSchemaFormItem {
  $type: 'mentions';
}

interface RateJsonSchemaFormItem
  extends filterCustomProps<RateProps>,
    BaseJsonSchemaFormItem {
  $type: 'rate';
}

interface SliderJsonSchemaFormItem
  extends filterCustomProps<SliderProps>,
    BaseJsonSchemaFormItem {
  $type: 'slider';
}

interface SwitchJsonSchemaFormItem
  extends filterCustomProps<SwitchProps>,
    BaseJsonSchemaFormItem {
  $type: 'switch';
}

interface UploadJsonSchemaFormItem
  extends filterCustomProps<UploadProps>,
    BaseJsonSchemaFormItem {
  $type: 'upload';
}

interface DraggerJsonSchemaFormItem
  extends filterCustomProps<DraggerProps>,
    BaseJsonSchemaFormItem {
  $type: 'dragger';
}

export type JsonSchemaFormItemType =
  // input
  | InputJsonSchemaFormItem
  | InputPasswordJsonSchemaFormItem
  | TextareaJsonSchemaFormItem
  | SearchJsonSchemaFormItem
  | OptJsonSchemaFormItem
  | InputNumberJsonSchemaFormItem
  | MentionsJsonSchemaFormItem
  | AutoCompleteJsonSchemaFormItem
  // select
  | SelectJsonSchemaFormItem
  | TreeSelectJsonSchemaFormItem
  | CascaderJsonSchemaFormItem
  // check
  | RadioJsonSchemaFormItem
  | CheckboxJsonSchemaFormItem
  | SegmentedJsonSchemaFormItem
  | RateJsonSchemaFormItem
  | SliderJsonSchemaFormItem
  | SwitchJsonSchemaFormItem
  // picker
  | DatePickerJsonSchemaFormItem
  | RangePickerJsonSchemaFormItem
  | TimePickerJsonSchemaFormItem
  | ColorPickerJsonSchemaFormItem
  // upload
  | UploadJsonSchemaFormItem
  | DraggerJsonSchemaFormItem;

export interface JsonSchemaFormProps {
  schema: JsonSchemaFormItemType[];
  formProps?: FormProps;
}

最后,在 JsonSchemaForm/index.ts 中对外暴露组件以及 ts 类型。

ts 复制代码
import JsonSchemaForm from './JsonSchemaForm';

export type { JsonSchemaFormItemType } from './type';

export default JsonSchemaForm;

在子包统一入口暴露组件以及 ts 类型

src/index.ts 中新增代码。

ts 复制代码
export { default as JsonSchemaForm } from './JsonSchemaForm';
export type { JsonSchemaFormItemType } from './JsonSchemaForm';

总结

至此,一个适配 antd 几乎所有内置组件的 Json 渲染表单已经完成,但是,在实际应用场景中 antd 内置的组件往往不够用,所以需要提供一个自定义的入口来让用户渲染自己想要的组件。

接下来,我们将会对 JsonSchemaForm 组件进行优化。

如果想提前知道更多内容可以直接查看github,欢迎大家 Star 和 PR,如有疑问可以评论或私信。

相关推荐
喜樂的CC44 分钟前
[react]Next.js之自适应布局和高清屏幕适配解决方案
javascript·react.js·postcss
天天扭码1 小时前
零基础 | 入门前端必备技巧——使用 DOM 操作插入 HTML 元素
前端·javascript·dom
咖啡虫1 小时前
css中的3d使用:深入理解 CSS Perspective 与 Transform-Style
前端·css·3d
拉不动的猪2 小时前
设计模式之------策略模式
前端·javascript·面试
旭久2 小时前
react+Tesseract.js实现前端拍照获取/选择文件等文字识别OCR
前端·javascript·react.js
独行soc2 小时前
2025年常见渗透测试面试题-红队面试宝典下(题目+回答)
linux·运维·服务器·前端·面试·职场和发展·csrf
uhakadotcom2 小时前
Google Earth Engine 机器学习入门:基础知识与实用示例详解
前端·javascript·面试
麓殇⊙2 小时前
Vue--组件练习案例
前端·javascript·vue.js
outstanding木槿2 小时前
React中 点击事件写法 的注意(this、箭头函数)
前端·javascript·react.js
会点php的前端小渣渣3 小时前
vue的计算属性computed的原理和监听属性watch的原理(新)
前端·javascript·vue.js