【antd design 5 Form.itme 封装】 持续更新......

 2024 / 1 / 3 日更新

// todo 组件只用于渲染 Form.Item 层 | 不包含 Form

// 调用示例
/* <FormItem<FormCodeType>
  label="项目名称"
  name="projectName"
  type="Input"
  input_disabled={modalStateText === "edit"}
  input_maxLength={64}
  style={{ width: 240 }}
  rules={[{ required: true, message: "请输入项目名称!" }]}
/>; */

import {
  AutoComplete,
  Button,
  Cascader,
  Checkbox,
  DatePicker,
  Form,
  Input,
  InputNumber,
  InputRef,
  QRCode,
  Radio,
  Select,
  Slider,
  Upload,
} from "antd";
import { Rule } from "antd/es/form";
import { SelectValue } from "antd/es/select";
import { RcFile } from "antd/es/upload";
import { Dayjs } from "dayjs";
import { ChangeEvent, KeyboardEvent } from "react";

const { RangePicker } = DatePicker;

type FormItemComponentType =
  | "Upload"
  | "Radio"
  | "Input"
  | "QRCode"
  | "Slider"
  | "Selector"
  | "Cascader"
  | "Checkbox"
  | "DatePicker"
  | "InputNumber"
  | "RangePicker"
  | "AutoComplete";

type CascaderOptionType = {
  label: React.ReactNode;
  value: string;
  disabled?: boolean;
  children?: Array<CascaderOptionType>;
};

type UploadType = {
  file: RcFile;
  data: any;
  filename: string;
  withCredentials: boolean;
  action: string;
  headers: { [key: string]: any };
  method: string;
  onSuccess: (response: any, file: RcFile) => void;
  onError: (error: Error, response: any, file: RcFile) => void;
  onProgress: (event: { percent: number }, file: RcFile) => void;
};

type GenericFormItemProps<T> = {
  type: FormItemComponentType;
  label: string;
  name: keyof T & string;
  rules: Array<Rule>;
  style?: React.CSSProperties;

  selector_showSearch?: boolean;
  selector_placeholder?: string;
  selector_options?: Array<{ value: string; label: string }>;
  selector_allowClear?: boolean;
  selector_disabled?: boolean;
  select_onChange?: (
    value: SelectValue,
    option:
      | { value: string; label: string }
      | { value: string; label: string }[],
  ) => void;

  input_ref?: React.RefObject<InputRef>;
  input_value?: string;
  input_border?: boolean;
  input_disabled?: boolean;
  input_maxLength?: number;
  input_showCount?: boolean;
  input_addonAfter?: string;
  input_placeholder?: string;
  input_defaultValue?: string;
  input_prefix?: React.ReactNode;
  input_suffix?: React.ReactNode;
  input_onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
  input_onPressEnter?: (e: KeyboardEvent<HTMLInputElement>) => void;

  date_placeholder?: string;
  date_picker?: "time" | "date" | "week" | "month" | "quarter" | "year";
  date_allowClear?: boolean;
  date_autoFocus?: boolean;
  date_bordered?: boolean;
  date_disabled?: boolean;
  date_showToday?: boolean;
  date_value?: Dayjs;
  date_defaultValue?: Dayjs;
  date_disabledDate?: (currentDate: Dayjs) => boolean;
  date_pickerOnChange?: (date: Dayjs | null, dateString: string) => void;

  inputNumber_min?: number;
  inputNumber_max?: number;
  inputNumber_disabled?: boolean;
  inputNumber_size?: "large" | "middle" | "small";

  radio_options?: Array<{ value: string; label: string }>;
  radio_value?: number;
  radio_disabled?: boolean;

  slider_min?: number;
  slider_max?: number;
  slider_disabled?: boolean;
  slider_tooltip?: boolean;

  autoComplete_placeholder?: string;
  autoComplete_disabled?: boolean;
  autoComplete_options?: Array<Record<string, string>>;
  autoComplete_maxlength?: number;

  cascader_options?: Array<CascaderOptionType>;
  cascader_disabled?: boolean;

  checkbox_options?: Array<{ value: string; label: string }>;
  checkbox_disabled?: boolean;

  QRCode_value?: string;

  upload_accept?: string;
  upload_fileList?: Array<string>;
  upload_button_title?: string;
  upload_button_disabled?: boolean;
  upload_showUploadList?: boolean;
  upload_button_icon?: React.ReactNode;
  upload_button_loading?: boolean;
  upload_button_type?: "link" | "text" | "default" | "primary" | "dashed";
  upload_customRequest?: (options: UploadType) => void;
};

const filterOption = (
  input: string,
  option?: { label: string; value: string | number },
): boolean => (option?.label ?? "").toLowerCase().includes(input.toLowerCase());

const FormItem = <T extends Record<string, any>>(
  PropsInfo: GenericFormItemProps<T>,
) => {
  // 基础信息
  const { type, label, name, rules, style } = PropsInfo;

  // 下拉框信息
  const {
    selector_options,
    selector_disabled,
    selector_allowClear,
    selector_showSearch,
    selector_placeholder,
    select_onChange,
  } = PropsInfo;

  // 输入框信息
  const {
    input_ref,
    input_value,
    input_border,
    input_prefix,
    input_suffix,
    input_disabled,
    input_maxLength,
    input_showCount,
    input_addonAfter,
    input_placeholder,
    input_defaultValue,
    input_onChange,
    input_onPressEnter,
  } = PropsInfo;

  // 日期选择器信息
  const {
    date_value,
    date_picker,
    date_disabled,
    date_bordered,
    date_showToday,
    date_autoFocus,
    date_allowClear,
    date_placeholder,
    date_defaultValue,
    date_disabledDate,
    date_pickerOnChange,
  } = PropsInfo;

  // 数字输入框信息
  const {
    inputNumber_min,
    inputNumber_max,
    inputNumber_size,
    inputNumber_disabled,
  } = PropsInfo;

  // 单选框 Radio 信息
  const { radio_options, radio_value, radio_disabled } = PropsInfo;

  // 移动数字滑块 信息
  const { slider_disabled, slider_min, slider_max, slider_tooltip } = PropsInfo;

  // 级联选择器信息
  const { cascader_options, cascader_disabled } = PropsInfo;

  // 输入框自动完成信息
  const {
    autoComplete_placeholder,
    autoComplete_options,
    autoComplete_disabled,
    autoComplete_maxlength,
  } = PropsInfo;

  // 复选框信息
  const { checkbox_options, checkbox_disabled } = PropsInfo;

  // 二维码信息
  const { QRCode_value } = PropsInfo;

  // upload 上传文件信息
  const {
    upload_accept,
    upload_fileList,
    upload_button_disabled,
    upload_button_icon,
    upload_button_type,
    upload_button_title,
    upload_showUploadList,
    upload_button_loading,
    upload_customRequest,
  } = PropsInfo;

  const renderItem = (): React.ReactNode => {
    switch (type) {
      case "Selector":
        return (
          <Select
            getPopupContainer={(): any =>
              document.getElementById("filterWrapper")
            }
            style={style}
            optionFilterProp="children"
            options={selector_options}
            disabled={selector_disabled ?? false}
            showSearch={selector_showSearch ?? true}
            allowClear={selector_allowClear ?? true}
            placeholder={selector_placeholder ?? ""}
            filterOption={filterOption}
            onChange={(value, option) =>
              select_onChange?.(
                value,
                Array.isArray(option) ? option : [option],
              )
            }
          />
        );

      case "Input":
        return (
          <Input
            style={style}
            ref={input_ref}
            value={input_value}
            prefix={input_prefix}
            suffix={input_suffix}
            bordered={input_border}
            disabled={input_disabled ?? false}
            showCount={input_showCount}
            maxLength={input_maxLength}
            addonAfter={input_addonAfter}
            placeholder={input_placeholder ?? ""}
            defaultValue={input_defaultValue}
            onChange={input_onChange}
            onPressEnter={input_onPressEnter}
          />
        );

      case "DatePicker":
        return (
          <DatePicker
            getPopupContainer={(): any =>
              document.getElementById("filterWrapper")
            }
            style={style}
            value={date_value}
            picker={date_picker}
            disabled={date_disabled}
            bordered={date_bordered}
            showToday={date_showToday ?? false}
            autoFocus={date_autoFocus}
            allowClear={date_allowClear ?? true}
            placeholder={date_placeholder ?? ""}
            defaultValue={date_defaultValue}
            onChange={date_pickerOnChange}
            disabledDate={date_disabledDate}
          />
        );

      case "AutoComplete":
        return (
          <AutoComplete
            getPopupContainer={(): any =>
              document.getElementById("filterWrapper")
            }
            maxLength={autoComplete_maxlength}
            style={style}
            disabled={autoComplete_disabled ?? false}
            options={autoComplete_options}
            placeholder={autoComplete_placeholder ?? ""}
            filterOption={(input_value, option) =>
              String(option!.value)
                .toUpperCase()
                .indexOf(input_value.toUpperCase()) !== -1
            }
          />
        );

      case "Upload":
        return (
          <Upload
            customRequest={upload_customRequest as any}
            accept={upload_accept}
            fileList={upload_fileList ?? ([] as any)}
            // disabled={upload_button_disabled}
            showUploadList={upload_showUploadList ?? false}
          >
            <Button
              loading={upload_button_loading}
              style={style}
              icon={upload_button_icon}
              disabled={upload_button_disabled ?? false}
              type={upload_button_type ?? "text"}
            >
              {upload_button_title ?? "上传"}
            </Button>
          </Upload>
        );

      case "RangePicker":
        return <RangePicker style={style} />;

      case "Radio":
        return (
          <Radio.Group
            style={style}
            disabled={radio_disabled ?? false}
            value={radio_value}
          >
            {radio_options?.map((item) => (
              <Radio key={item.value} value={item.value}>
                {item.label}
              </Radio>
            ))}
          </Radio.Group>
        );

      case "InputNumber":
        return (
          <InputNumber
            style={style}
            size={inputNumber_size ?? "small"}
            disabled={inputNumber_disabled ?? false}
            min={inputNumber_min}
            max={inputNumber_max}
          />
        );

      case "Slider":
        return (
          <Slider
            style={style}
            min={slider_min}
            max={slider_max}
            disabled={slider_disabled ?? false}
            tooltip={{ open: slider_tooltip ?? false }}
          />
        );

      case "Cascader":
        return (
          <Cascader
            style={style}
            getPopupContainer={(): any =>
              document.getElementById("filterWrapper")
            }
            disabled={cascader_disabled ?? false}
            options={cascader_options}
            expandTrigger="hover"
          />
        );

      case "Checkbox":
        return (
          <Checkbox.Group
            style={style}
            disabled={checkbox_disabled ?? false}
            options={checkbox_options}
          />
        );

      case "QRCode":
        return <QRCode style={style} value={QRCode_value || "-"} />;

      default:
        return <div style={{ color: "red" }}>传入的 type 不存在,请检查</div>;
    }
  };

  return (
    <Form.Item label={label} name={name as any} rules={rules}>
      {renderItem()}
    </Form.Item>
  );
};

export default FormItem;

2024 1.9 日更新 ( 拆分 TS 类型 ,简化代码 )

Type.ts

import { InputRef } from "antd";
import { Rule } from "antd/es/form";
import { SelectValue } from "antd/es/select";
import { RcFile } from "antd/es/upload";
import { Dayjs } from "dayjs";
import { ChangeEvent, KeyboardEvent } from "react";

export type FormItemComponentType =
  | "Upload"
  | "Radio"
  | "Input"
  | "QRCode"
  | "Slider"
  | "Selector"
  | "Cascader"
  | "Checkbox"
  | "DatePicker"
  | "InputNumber"
  | "RangePicker"
  | "AutoComplete";

export type CascaderOptionType = {
  value: string;
  disabled?: boolean;
  children?: Array<CascaderOptionType>;
} & { [key in "label"]: React.ReactNode };

export type UploadType = {
  file: RcFile;
  data: any;
  filename: string;
  withCredentials: boolean;
  action: string;
  headers: { [key: string]: any };
  method: string;
  onSuccess: (response: any, file: RcFile) => void;
  onError: (error: Error, response: any, file: RcFile) => void;
  onProgress: (event: { percent: number }, file: RcFile) => void;
};

type date_picker_type = "time" | "date" | "week" | "month" | "quarter" | "year";
type upload_button_type = "link" | "text" | "default" | "primary" | "dashed";
type inputNumber_size_type = "large" | "middle" | "small";

export type GenericFormItemProps<T> = {
  type: FormItemComponentType;
  label: string;
  name: keyof T & string;
  rules: Array<Rule>;
  style?: React.CSSProperties;
  input_ref?: React.RefObject<InputRef>;
  date_picker?: date_picker_type;
  inputNumber_size?: inputNumber_size_type;
  autoComplete_options?: Array<Record<string, string>>;
  cascader_options?: Array<CascaderOptionType>;
  upload_fileList?: Array<string>;
  upload_button_type?: upload_button_type;
  select_onChange?: (
    value: SelectValue,
    option:
      | { value: string; label: string }
      | { value: string; label: string }[],
  ) => void;
  upload_customRequest?: (options: UploadType) => void;
  date_pickerOnChange?: (date: Dayjs | null, dateString: string) => void;
  date_disabledDate?: (currentDate: Dayjs) => boolean;
  input_onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
  input_onPressEnter?: (e: KeyboardEvent<HTMLInputElement>) => void;
} & {
  [key in
    | "selector_showSearch"
    | "selector_allowClear"
    | "selector_disabled"
    | "input_border"
    | "input_showCount"
    | "date_allowClear"
    | "date_autoFocus"
    | "date_bordered"
    | "date_disabled"
    | "date_showToday"
    | "inputNumber_disabled"
    | "radio_disabled"
    | "slider_disabled"
    | "slider_tooltip"
    | "autoComplete_disabled"
    | "cascader_disabled"
    | "checkbox_disabled"
    | "upload_button_disabled"
    | "upload_showUploadList"
    | "upload_button_loading"
    | "input_disabled"]?: boolean;
} & {
  [key in
    | "input_value"
    | "input_addonAfter"
    | "input_placeholder"
    | "input_defaultValue"
    | "date_placeholder"
    | "autoComplete_placeholder"
    | "QRCode_value"
    | "upload_accept"
    | "selector_placeholder"
    | "upload_button_title"]?: string;
} & {
  [key in
    | "input_maxLength"
    | "inputNumber_min"
    | "inputNumber_max"
    | "radio_value"
    | "slider_min"
    | "slider_max"
    | "autoComplete_maxlength"]?: number;
} & {
  [key in "selector_options" | "radio_options" | "checkbox_options"]?: Array<{
    value: string;
    label: string;
  }>;
} & {
  [key in
    | "input_prefix"
    | "input_suffix"
    | "upload_button_icon"]?: React.ReactNode;
} & { [key in "date_value" | "date_defaultValue"]?: Dayjs };

FormItme:

// todo 组件只用于渲染 Form.Item 层 | 不包含 Form

// 调用示例
/* <FormItem<FormCodeType>
  label="项目名称"
  name="projectName"
  type="Input"
  input_disabled={modalStateText === "edit"}
  input_maxLength={64}
  style={{ width: 240 }}
  rules={[{ required: true, message: "请输入项目名称!" }]}
/>; */

import {
  AutoComplete,
  Button,
  Cascader,
  Checkbox,
  DatePicker,
  Form,
  Input,
  InputNumber,
  QRCode,
  Radio,
  Select,
  Slider,
  Upload,
} from "antd";
import { GenericFormItemProps } from "./type";

const { RangePicker } = DatePicker;

const filterOption = (
  input: string,
  option?: { label: string; value: string | number },
): boolean => (option?.label ?? "").toLowerCase().includes(input.toLowerCase());

const FormItem = <T extends Record<string, any>>(
  PropsInfo: GenericFormItemProps<T>,
) => {
  // 基础信息
  const { type, label, name, rules, style } = PropsInfo;

  // 下拉框信息
  const {
    selector_options,
    selector_disabled,
    selector_allowClear,
    selector_showSearch,
    selector_placeholder,
    select_onChange,
  } = PropsInfo;

  // 输入框信息
  const {
    input_ref,
    input_value,
    input_border,
    input_prefix,
    input_suffix,
    input_disabled,
    input_maxLength,
    input_showCount,
    input_addonAfter,
    input_placeholder,
    input_defaultValue,
    input_onChange,
    input_onPressEnter,
  } = PropsInfo;

  // 日期选择器信息
  const {
    date_value,
    date_picker,
    date_disabled,
    date_bordered,
    date_showToday,
    date_autoFocus,
    date_allowClear,
    date_placeholder,
    date_defaultValue,
    date_disabledDate,
    date_pickerOnChange,
  } = PropsInfo;

  // 数字输入框信息
  const {
    inputNumber_min,
    inputNumber_max,
    inputNumber_size,
    inputNumber_disabled,
  } = PropsInfo;

  // 单选框 Radio 信息
  const { radio_options, radio_value, radio_disabled } = PropsInfo;

  // 移动数字滑块 信息
  const { slider_disabled, slider_min, slider_max, slider_tooltip } = PropsInfo;

  // 级联选择器信息
  const { cascader_options, cascader_disabled } = PropsInfo;

  // 输入框自动完成信息
  const {
    autoComplete_placeholder,
    autoComplete_options,
    autoComplete_disabled,
    autoComplete_maxlength,
  } = PropsInfo;

  // 复选框信息
  const { checkbox_options, checkbox_disabled } = PropsInfo;

  // 二维码信息
  const { QRCode_value } = PropsInfo;

  // upload 上传文件信息
  const {
    upload_accept,
    upload_fileList,
    upload_button_disabled,
    upload_button_icon,
    upload_button_type,
    upload_button_title,
    upload_showUploadList,
    upload_button_loading,
    upload_customRequest,
  } = PropsInfo;

  const renderItem = (): React.ReactNode => {
    switch (type) {
      case "Selector":
        return (
          <Select
            getPopupContainer={(): any =>
              document.getElementById("filterWrapper")
            }
            style={style}
            optionFilterProp="children"
            options={selector_options}
            disabled={selector_disabled ?? false}
            showSearch={selector_showSearch ?? true}
            allowClear={selector_allowClear ?? true}
            placeholder={selector_placeholder ?? ""}
            filterOption={filterOption}
            onChange={(value, option) =>
              select_onChange?.(
                value,
                Array.isArray(option) ? option : [option],
              )
            }
          />
        );

      case "Input":
        return (
          <Input
            style={style}
            ref={input_ref}
            value={input_value}
            prefix={input_prefix}
            suffix={input_suffix}
            bordered={input_border}
            disabled={input_disabled ?? false}
            showCount={input_showCount}
            maxLength={input_maxLength}
            addonAfter={input_addonAfter}
            placeholder={input_placeholder ?? ""}
            defaultValue={input_defaultValue}
            onChange={input_onChange}
            onPressEnter={input_onPressEnter}
          />
        );

      case "DatePicker":
        return (
          <DatePicker
            getPopupContainer={(): any =>
              document.getElementById("filterWrapper")
            }
            style={style}
            value={date_value}
            picker={date_picker}
            disabled={date_disabled}
            bordered={date_bordered}
            showToday={date_showToday ?? false}
            autoFocus={date_autoFocus}
            allowClear={date_allowClear ?? true}
            placeholder={date_placeholder ?? ""}
            defaultValue={date_defaultValue}
            onChange={date_pickerOnChange}
            disabledDate={date_disabledDate}
          />
        );

      case "AutoComplete":
        return (
          <AutoComplete
            getPopupContainer={(): any =>
              document.getElementById("filterWrapper")
            }
            maxLength={autoComplete_maxlength}
            style={style}
            disabled={autoComplete_disabled ?? false}
            options={autoComplete_options}
            placeholder={autoComplete_placeholder ?? ""}
            filterOption={(input_value, option) =>
              String(option!.value)
                .toUpperCase()
                .indexOf(input_value.toUpperCase()) !== -1
            }
          />
        );

      case "Upload":
        return (
          <Upload
            customRequest={upload_customRequest as any}
            accept={upload_accept}
            fileList={upload_fileList ?? ([] as any)}
            multiple
            // disabled={upload_button_disabled}
            showUploadList={upload_showUploadList ?? false}
          >
            <Button
              loading={upload_button_loading}
              style={style}
              icon={upload_button_icon}
              disabled={upload_button_disabled ?? false}
              type={upload_button_type ?? "text"}
            >
              {upload_button_title ?? "上传"}
            </Button>
          </Upload>
        );

      case "RangePicker":
        return <RangePicker style={style} />;

      case "Radio":
        return (
          <Radio.Group
            style={style}
            disabled={radio_disabled ?? false}
            value={radio_value}
          >
            {radio_options?.map((item) => (
              <Radio key={item.value} value={item.value}>
                {item.label}
              </Radio>
            ))}
          </Radio.Group>
        );

      case "InputNumber":
        return (
          <InputNumber
            style={style}
            size={inputNumber_size ?? "small"}
            disabled={inputNumber_disabled ?? false}
            min={inputNumber_min}
            max={inputNumber_max}
          />
        );

      case "Slider":
        return (
          <Slider
            style={style}
            min={slider_min}
            max={slider_max}
            disabled={slider_disabled ?? false}
            tooltip={{ open: slider_tooltip ?? false }}
          />
        );

      case "Cascader":
        return (
          <Cascader
            style={style}
            getPopupContainer={(): any =>
              document.getElementById("filterWrapper")
            }
            disabled={cascader_disabled ?? false}
            options={cascader_options}
            expandTrigger="hover"
          />
        );

      case "Checkbox":
        return (
          <Checkbox.Group
            style={style}
            disabled={checkbox_disabled ?? false}
            options={checkbox_options}
          />
        );

      case "QRCode":
        return <QRCode style={style} value={QRCode_value || "-"} />;

      default:
        return <div style={{ color: "red" }}>传入的 type 不存在,请检查</div>;
    }
  };

  return (
    <Form.Item label={label} name={name as any} rules={rules}>
      {renderItem()}
    </Form.Item>
  );
};

export default FormItem;

2024 / 1 / 31 日更新

import { InputRef } from "antd";
import { Rule } from "antd/es/form";
import { SelectValue } from "antd/es/select";
import { RcFile } from "antd/es/upload";
import { Dayjs } from "dayjs";
import { ChangeEvent, KeyboardEvent } from "react";

export type FormItemComponentType =
  | "Radio"
  | "Input"
  | "Slider"
  | "QRCode"
  | "Upload"
  | "Selector"
  | "Cascader"
  | "Checkbox"
  | "TextArea"
  | "DatePicker"
  | "InputNumber"
  | "RangePicker"
  | "AutoComplete";

export type CascaderOptionType = {
  value: string;
  disabled?: boolean;
  children?: Array<CascaderOptionType>;
} & { [key in "label"]: React.ReactNode };

export type UploadType = {
  data: any;
  file: RcFile;
  method: string;
  action: string;
  filename: string;
  withCredentials: boolean;
  headers: { [key: string]: any };
  onSuccess: (response: any, file: RcFile) => void;
  onError: (error: Error, response: any, file: RcFile) => void;
  onProgress: (event: { percent: number }, file: RcFile) => void;
};

type date__picker__type =
  | "time"
  | "date"
  | "week"
  | "year"
  | "month"
  | "quarter";
type inputNumber__size__type = "large" | "middle" | "small";
type upload__button__type = "link" | "text" | "default" | "primary" | "dashed";

export type GenericFormItemProps<T> = {
  label: string;
  rules: Array<Rule>;
  name: keyof T & string;
  type: FormItemComponentType;
  style?: React.CSSProperties;
  upload__fileList?: Array<string>;
  date__picker?: date__picker__type;
  input__ref?: React.RefObject<InputRef>;
  inputNumber__size?: inputNumber__size__type;
  upload__button__type?: upload__button__type;
  cascader__options?: Array<CascaderOptionType>;
  autoComplete__options?: Array<Record<string, string>>;
  date__disabledDate?: (currentDate: Dayjs) => boolean;
  upload__customRequest?: (options: UploadType) => void;
  input__onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
  input__onPressEnter?: (e: KeyboardEvent<HTMLInputElement>) => void;
  date__pickerOnChange?: (date: Dayjs | null, dateString: string) => void;
  select__onChange?: (
    value: SelectValue,
    option:
      | { value: string | any; label: string }
      | { value: string | any; label: string }[]
  ) => void;
} & {
  [key in
    | "input__border"
    | "date__bordered"
    | "date__disabled"
    | "radio__disabled"
    | "date__showToday"
    | "date__autoFocus"
    | "slider__tooltip"
    | "input__showCount"
    | "date__allowClear"
    | "slider__disabled"
    | "upload__multiple"
    | "cascader__disabled"
    | "checkbox__disabled"
    | "selector__disabled"
    | "textArea__showCount"
    | "selector__allowClear"
    | "selector__showSearch"
    | "inputNumber__disabled"
    | "autoComplete__disabled"
    | "upload__showUploadList"
    | "upload__button__loading"
    | "upload__button__disabled"
    | "textArea__style__stretch"
    | "input__disabled"]?: boolean;
} & {
  [key in
    | "input__value"
    | "QRCode__value"
    | "upload__accept"
    | "date__placeholder"
    | "input__addonAfter"
    | "input__placeholder"
    | "input__defaultValue"
    | "selector__placeholder"
    | "textArea__placeholder"
    | "autoComplete__placeholder"
    | "upload__button__title"]?: string;
} & {
  [key in
    | "slider__max"
    | "slider__min"
    | "radio__value"
    | "textArea__row"
    | "input__maxLength"
    | "inputNumber__min"
    | "inputNumber__max"
    | "textArea__maxLength"
    | "autoComplete__maxlength"]?: number;
} & {
  [key in "radio__options" | "selector__options" | "checkbox__options"]?: {
    label: string;
    value: string | boolean;
  }[];
} & {
  [key in
    | "input__prefix"
    | "input__suffix"
    | "upload__button__icon"]?: React.ReactNode;
} & { [key in "date__value" | "date__defaultValue"]?: Dayjs };
// todo 组件只用于渲染 Form.Item 层 | 不包含 Form

// 调用示例
/* <FormItem<FormFieldsNameType>
  label="项目名称"
  name="projectName"
  type="Input"
  input__disabled={modalStateText === "edit"}
  input__maxLength={64}
  style={{ width: 240 }}
  rules={[{ required: true, message: "请输入项目名称!" }]}
/>; */

import {
  AutoComplete,
  Button,
  Cascader,
  Checkbox,
  DatePicker,
  Form,
  Input,
  InputNumber,
  QRCode,
  Radio,
  Select,
  Slider,
  Upload,
} from "antd";
import TextArea from "antd/es/input/TextArea";
import { GenericFormItemProps } from "./type";

const { RangePicker } = DatePicker;

// 下拉框可搜索函数
const filterOption = (
  input: string,
  option?: { label: string; value: string | number }
): boolean => (option?.label ?? "").toLowerCase().includes(input.toLowerCase());

const FormItem = <T extends Record<string, any>>(
  PropsInfo: GenericFormItemProps<T>
) => {
  // 基础信息
  const { type, label, name, rules, style } = PropsInfo;

  // 下拉框信息
  const {
    selector__options,
    selector__disabled,
    selector__allowClear,
    selector__showSearch,
    selector__placeholder,
    select__onChange,
  } = PropsInfo;

  // 输入框信息
  const {
    input__ref,
    input__value,
    // input__border,
    input__prefix,
    input__suffix,
    input__disabled,
    input__maxLength,
    input__showCount,
    input__addonAfter,
    input__placeholder,
    input__defaultValue,
    input__onChange,
    input__onPressEnter,
  } = PropsInfo;

  // 日期选择器信息
  const {
    date__value,
    date__picker,
    date__disabled,
    // date__bordered,
    date__showToday,
    date__autoFocus,
    date__allowClear,
    date__placeholder,
    date__defaultValue,
    date__disabledDate,
    date__pickerOnChange,
  } = PropsInfo;

  // 数字输入框信息
  const {
    inputNumber__min,
    inputNumber__max,
    inputNumber__size,
    inputNumber__disabled,
  } = PropsInfo;

  // 单选框 Radio 信息
  const { radio__options, radio__value, radio__disabled } = PropsInfo;

  // 移动数字滑块 信息
  const { slider__disabled, slider__min, slider__max, slider__tooltip } =
    PropsInfo;

  // 级联选择器信息
  const { cascader__options, cascader__disabled } = PropsInfo;

  // 输入框自动完成信息
  const {
    autoComplete__options,
    autoComplete__disabled,
    autoComplete__maxlength,
    autoComplete__placeholder,
  } = PropsInfo;

  // 复选框信息
  const { checkbox__options, checkbox__disabled } = PropsInfo;

  // 文本域信息
  const {
    textArea__row,
    textArea__showCount,
    textArea__maxLength,
    textArea__placeholder,
    textArea__style__stretch,
  } = PropsInfo;

  // 二维码信息
  const { QRCode__value } = PropsInfo;

  // upload 上传文件信息
  const {
    upload__accept,
    upload__multiple,
    upload__fileList,
    upload__button__icon,
    upload__button__type,
    upload__button__title,
    upload__showUploadList,
    upload__button__loading,
    upload__button__disabled,
    upload__customRequest,
  } = PropsInfo;

  const renderItem = (): React.ReactNode => {
    switch (type) {
      case "Selector":
        return (
          <Select
            style={style}
            optionFilterProp="children"
            filterOption={filterOption}
            disabled={selector__disabled ?? false}
            showSearch={selector__showSearch ?? true}
            allowClear={selector__allowClear ?? true}
            placeholder={selector__placeholder ?? ""}
            options={selector__options as { label: string; value: string }[]}
            onChange={(value, option) =>
              select__onChange?.(
                value,
                Array.isArray(option) ? option : [option]
              )
            }
          />
        );

      case "TextArea":
        return (
          <TextArea
            rows={textArea__row ?? 3}
            maxLength={textArea__maxLength ?? 50}
            showCount={textArea__showCount ?? true}
            placeholder={textArea__placeholder ?? ""}
            style={textArea__style__stretch ? { resize: "none" } : undefined}
          />
        );

      case "Input":
        return (
          <Input
            style={style}
            ref={input__ref}
            value={input__value}
            prefix={input__prefix}
            suffix={input__suffix}
            // bordered={input__border}
            showCount={input__showCount}
            maxLength={input__maxLength}
            addonAfter={input__addonAfter}
            defaultValue={input__defaultValue}
            disabled={input__disabled ?? false}
            placeholder={input__placeholder ?? ""}
            onChange={input__onChange}
            onPressEnter={input__onPressEnter}
          />
        );

      case "DatePicker":
        return (
          <DatePicker
            style={style}
            value={date__value}
            picker={date__picker}
            disabled={date__disabled}
            // bordered={date__bordered}
            autoFocus={date__autoFocus}
            defaultValue={date__defaultValue}
            showToday={date__showToday ?? false}
            allowClear={date__allowClear ?? true}
            placeholder={date__placeholder ?? ""}
            onChange={date__pickerOnChange}
            disabledDate={date__disabledDate}
          />
        );

      case "AutoComplete":
        return (
          <AutoComplete
            style={style}
            options={autoComplete__options}
            maxLength={autoComplete__maxlength}
            disabled={autoComplete__disabled ?? false}
            placeholder={autoComplete__placeholder ?? ""}
            filterOption={(input__value, option) =>
              String(option!.value)
                .toUpperCase()
                .indexOf(input__value.toUpperCase()) !== -1
            }
          />
        );

      case "Upload":
        return (
          <Upload
            accept={upload__accept}
            multiple={upload__multiple ?? true}
            fileList={upload__fileList ?? ([] as any)}
            customRequest={upload__customRequest as any}
            showUploadList={upload__showUploadList ?? false}
          >
            <Button
              style={style}
              icon={upload__button__icon}
              type={upload__button__type ?? "text"}
              loading={upload__button__loading ?? false}
              disabled={upload__button__disabled ?? false}
            >
              {upload__button__title ?? "上传"}
            </Button>
          </Upload>
        );

      case "RangePicker":
        return <RangePicker style={style} />;

      case "Radio":
        return (
          <Radio.Group
            style={style}
            value={radio__value}
            disabled={radio__disabled ?? false}
          >
            {(
              radio__options ?? [
                { value: "radio__options 无值", label: "radio__options 无值" },
              ]
            ).map((item) => (
              <Radio key={`${item.value}`} value={item.value}>
                {item.label}
              </Radio>
            ))}
          </Radio.Group>
        );

      case "InputNumber":
        return (
          <InputNumber
            style={style}
            min={inputNumber__min}
            max={inputNumber__max}
            size={inputNumber__size ?? "small"}
            disabled={inputNumber__disabled ?? false}
          />
        );

      case "Slider":
        return (
          <Slider
            style={style}
            min={slider__min}
            max={slider__max}
            disabled={slider__disabled ?? false}
            tooltip={{ open: slider__tooltip ?? false }}
          />
        );

      case "Cascader":
        return (
          <Cascader
            style={style}
            expandTrigger="hover"
            options={cascader__options}
            disabled={cascader__disabled ?? false}
          />
        );

      case "Checkbox":
        return (
          <Checkbox.Group
            style={style}
            disabled={checkbox__disabled ?? false}
            options={
              checkbox__options ?? [
                {
                  value: "checkbox__options 无值",
                  label: "checkbox__options 无值",
                },
              ]
            }
          />
        );

      case "QRCode":
        return <QRCode style={style} value={QRCode__value || "-"} />;

      default:
        return <div style={{ color: "red" }}>传入的 type 不存在,请检查</div>;
    }
  };

  return (
    <Form.Item label={label} name={name as any} rules={rules}>
      {renderItem()}
    </Form.Item>
  );
};

export default FormItem;

时小记,终有成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值