React业务积累,如何打造一个适应Antd的表单项组件(二)?

158 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

本篇其实没啥内容,主要就是对前面一文的完整代码展示,避免前文章内容主次不清

完整代码如下:

ps: 可以直接将代码复制粘贴,安装好依赖即可运行看效果哦~

组件代码

import type { ReactElement, ReactNode } from 'react';
import React, { useEffect, useMemo, useState } from 'react';

import { CloseOutlined, PlusCircleOutlined } from '@ant-design/icons';
import type { DrawerFormProps, ModalFormProps } from '@ant-design/pro-form';
import { DrawerForm, ModalForm } from '@ant-design/pro-form';
import type { ProTableProps } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import { Button, Space } from 'antd';
import { isArray } from 'lodash';

type showTypeEnum = 'button' | 'input';

interface handleValueType<T, S> {
  (selectedVal: T): S;
}
/**
 * T 表示数据行类型
 * S 表示数据行转换成value的类型
 */
export type FormListSelectProps<T = unknown, S = unknown | T> = {
  // handleValue?:handleValueType<T,S>
  valueDispose?: {
    tag: keyof S | string[]; // 显示值的字段
    key: keyof S | string[]; // 显示值的key
    handleValue: handleValueType<T, S>;
  };
  title?: string;
  multiple?: boolean;
  showType?: showTypeEnum;
  triggerType?: 'modal' | 'drawer';
  triggerForm?: ModalFormProps & DrawerFormProps;
  tableProps?: ProTableProps<T, Record<string, unknown>>;
  isTableSearch?: boolean;
  onChange?: (v: S | S[]) => void;
  children?: ReactNode;
  value?: S[];
  defaultValue?: S[];
};

function FormListSelect<T = Record<string, unknown>, S = T>(props: FormListSelectProps<T, S>) {
  const [selectValArray, setSelectValArray] = useState<S[]>([]);
  const {
    tableProps,
    title,
    multiple,
    isTableSearch,
    showType,
    onChange,
    triggerType,
    triggerForm,
    valueDispose,
    defaultValue,
  } = props;

  const triggerTypeElementMap = {
    modal: ModalForm,
    drawer: DrawerForm,
  };
  const RenderTrigger = triggerTypeElementMap[props.triggerType || 'modal'];

  const selectKeyArray = useMemo<(string | number)[]>(() => {
    if (!selectValArray) {
      return [];
    }
    const result = selectValArray.map((item: S | T) => {
      if (valueDispose) {
        if (isArray(valueDispose.key)) {
          let res = item;
          valueDispose.key.forEach((its) => {
            console.log(its, item[its]);
            res = res[its];
          });
          return res;
        } else {
          return item[valueDispose.key as string];
        }
      } else {
        return item;
      }
    });
    return result;
  }, [selectValArray]);

  useEffect(() => {
    if (props.value) {
      setSelectValArray(Array.isArray(props.value) ? props.value : [props.value]);
    }
  }, [props.value]);

  useEffect(() => {
    if (defaultValue) {
      setSelectValArray(Array.isArray(defaultValue) ? defaultValue : [defaultValue]);
    }
  }, [defaultValue]);

  const fromTiggerRender = (value: (S | T)[], type: showTypeEnum = 'button', tagfiled: keyof S | string[]) => {
    if (type === 'button') {
      return (
        <div>
          <Space style={{ display: value.length !== 0 ? 'inline-flex' : 'none' }}>
            {value.map((item) => {
              const timeStr = new Date().getTime();
              if (typeof item !== 'object') {
                return (
                  <Button key={timeStr} size="small" type="dashed">
                    <Space>
                      {item}
                      <CloseOutlined />
                    </Space>
                  </Button>
                );
              } else {
                let showTag = item;
                if (isArray(tagfiled)) {
                  tagfiled.forEach((tagf) => {
                    showTag = showTag[tagf];
                  });
                } else {
                  showTag = showTag[tagfiled as string];
                }
                return (
                  <Button size="small" type="dashed" key={timeStr}>
                    <Space>
                      {showTag}
                      <CloseOutlined />
                    </Space>
                  </Button>
                );
              }
            })}
          </Space>
          <Space style={{ display: value.length == 0 ? 'inline-flex' : 'none' }}>
            <Button>
              <Space>
                {title}
                <PlusCircleOutlined />
              </Space>
            </Button>
          </Space>
        </div>
      );
    } else {
      return <></>;
    }
  };
  let TiggerProps = {};
  if (triggerType === 'modal') {
    TiggerProps = Object.assign(triggerForm ? triggerForm : {}, {
      modalProps: {
        bodyStyle: {
          maxHeight: '65vh',
          overflow: 'auto',
        },
        ...triggerForm?.modalProps,
      },
    });
  } else {
    TiggerProps = triggerForm ? triggerForm : {};
  }
  const CustomNode = props.children;
  return RenderTrigger ? (
    <RenderTrigger
      title={title}
      {...TiggerProps}
      onFinish={async () => {
        console.log(selectValArray, '触发提交');
        if (onChange) onChange(multiple ? selectValArray : selectValArray?.[0]);
        return true;
      }}
      trigger={fromTiggerRender(selectValArray, showType, valueDispose?.tag || [])}
    >
      {!CustomNode ? (
        <ProTable
          toolBarRender={false}
          {...tableProps}
          rowSelection={
            multiple
              ? {
                  type: 'checkbox',
                  onSelect(_, __, selectedRows) {
                    const res = selectedRows.map((item) => {
                      if (valueDispose) return valueDispose.handleValue(item);
                      return item as unknown as S;
                    });
                    setSelectValArray(() => {
                      return res;
                    });
                  },
                  selectedRowKeys: selectKeyArray,
                }
              : {
                  type: 'radio',
                  onSelect(_, __, selectedRows) {
                    const res = selectedRows.map((item) => {
                      if (valueDispose) return valueDispose.handleValue(item);
                      return item as unknown as S;
                    });
                    setSelectValArray(() => {
                      return res;
                    });
                  },
                  selectedRowKeys: selectKeyArray,
                }
          }
          pagination={{
            defaultCurrent: 1,
            defaultPageSize: 10,
            showQuickJumper: true,
          }}
          search={
            isTableSearch
              ? {
                  collapseRender: false,
                  collapsed: false,
                  labelWidth: 'auto',
                  ...tableProps?.search,
                }
              : false
          }
        />
      ) : (
        React.cloneElement(CustomNode as ReactElement, {
          onChange(value: T) {
            console.log(value, 'afasdfa');
            let valueRes: S | S[];
            if (valueDispose) {
              valueRes = isArray(value)
                ? value.map((vs: T) => {
                    return valueDispose.handleValue(vs);
                  })
                : valueDispose.handleValue(value);
            } else {
              valueRes = value as unknown as S | S[];
            }
            setSelectValArray(() => {
              return isArray(valueRes) ? valueRes : [valueRes];
            });
          },
          value: selectValArray,
        })
      )}
    </RenderTrigger>
  ) : (
    <></>
  );
}

FormListSelect.defaultProps = {
  title: '标题',
  triggerType: 'modal',
  showType: 'button',
  multiple: false,
  isTableSearch: false,
  tagLabelField: 'name',
  handleValue: (p: unknown) => p,
};

export default FormListSelect;

后记

欢迎大家指出问题并改进