import React, { useContext, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; import type { ParamsType } from '@ant-design/pro-provider'; import type { ButtonProps } from 'antd'; import { Button, Form } from 'antd'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import { PlusOutlined } from '@ant-design/icons'; import { runFunction } from '@ant-design/pro-utils'; import ProTable from '../../Table'; import type { ProTableProps, ActionType } from '../../typing'; import type { GetRowKey } from 'antd/lib/table/interface'; export type RecordCreatorProps = { record: T | ((index: number) => T); position?: 'top' | 'bottom'; /** * 新增一行的类型 * * @augments dataSource 将会新增一行数据到 dataSource 中,不支持取消,只能删除 * @augments cache 将会把数据放到缓存中,取消后消失 */ newRecordType?: 'dataSource' | 'cache'; }; export type EditableProTableProps = Omit< ProTableProps, 'onChange' > & { value?: T[]; onChange?: (value: T[]) => void; /** @name 原先的 table OnChange */ onTableChange?: ProTableProps['onChange']; /** @name 新建按钮的设置 */ recordCreatorProps?: | (RecordCreatorProps & ButtonProps & { creatorButtonText?: React.ReactNode; }) | false; /** 最大行数 */ maxLength?: number; /** Table 的值发生改变,为了适应 Form 调整了顺序 */ onValuesChange?: (values: T[], record: T) => void; /** 是否受控,如果为 true,每次 value 更新都会重置表单 */ controlled?: boolean; }; const EditableTableActionContext = React.createContext | undefined>(undefined); /** 可编辑表格的按钮 */ function RecordCreator(props: RecordCreatorProps & { children: JSX.Element }) { const { children, record, position, newRecordType } = props; const actionRef = useContext(EditableTableActionContext); return React.cloneElement(children, { ...children.props, onClick: (e: any) => { actionRef?.current?.addEditRecord(record, { position, newRecordType }); children.props.onClick?.(e); }, }); } /** * 可以直接放到 Form 中的可编辑表格 * * @param props */ function EditableTable, U extends ParamsType = ParamsType>( props: EditableProTableProps, ) { const { onTableChange, maxLength, recordCreatorProps, rowKey, controlled, ...rest } = props; const actionRef = useRef(); const [form] = Form.useForm(); // 设置 ref useImperativeHandle(rest.actionRef, () => actionRef.current); const [value, setValue] = useMergedState(() => props.value || [], { value: props.value, onChange: props.onChange, }); const getRowKey = React.useMemo>((): GetRowKey => { if (typeof rowKey === 'function' && rowKey) { return rowKey; } return (record: T, index?: number) => (record as any)[rowKey as string] || index; }, [rowKey]); useEffect(() => { if (!props.controlled) return; value.forEach((current, index) => { form.setFieldsValue({ [getRowKey(current, index)]: current, }); }, {}); // eslint-disable-next-line react-hooks/exhaustive-deps }, [value, props.controlled]); const { record, position, creatorButtonText, newRecordType, ...restButtonProps } = recordCreatorProps || {}; const isTop = position === 'top'; const creatorButtonDom = useMemo(() => { if (maxLength && maxLength <= value?.length) { return false; } return ( recordCreatorProps !== false && ( ) ); // eslint-disable-next-line react-hooks/exhaustive-deps }, [recordCreatorProps, maxLength, value.length]); const buttonRenderProps = useMemo(() => { if (!creatorButtonDom) { return {}; } if (isTop) { return { components: { header: { wrapper: ({ className, children, }: { className: string; children: React.ReactNode; }) => ( {children} {/* 占位 */} {creatorButtonDom} {creatorButtonDom} ), }, }, }; } return { tableViewRender: (_: any, dom: any) => { return ( <> {props.tableViewRender?.(_, dom) ?? dom} {creatorButtonDom} ); }, }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [isTop, creatorButtonDom]); return ( search={false} options={false} pagination={false} rowKey={rowKey} {...rest} {...buttonRenderProps} tableLayout="fixed" actionRef={actionRef} onChange={onTableChange} dataSource={value} editable={{ form, ...props.editable, onValuesChange: props?.onValuesChange || props.editable?.onValuesChange || // 受控模式需要触发 onchange (props.controlled && props?.onChange) ? (r: T, dataSource: T[]) => { props.editable?.onValuesChange?.(r, dataSource); props.onValuesChange?.(dataSource, r); if (props.controlled) { props?.onChange?.(dataSource); } } : undefined, }} onDataSourceChange={setValue} /> ); } EditableTable.RecordCreator = RecordCreator; export default EditableTable;