基于AntDesign二次封装table组件
二次封装table组件
我基于AntDesign当中的table完成了table的二次封装,它主要有以下几个功能:
-
展示数据
-
提供批量删除的方法进行批量删除
-
提供删除按钮,进行删除操作
-
提供导出单个为excel表格的接口,进行excel导出操作
-
-
提供编辑按钮,进行编辑操作
-
提供添加按钮,进行添加操作
大致的步骤:
1、类型定义部分
(1)表格配置类型:
// 1、定义表格配置类型
interface TableConfig<T> {url: string;columns: TableProps<T>["columns"];requestParams?: Record<string, any>;// 添加行选择相关属性rowSelection?: boolean;rowKey?: string;// 自定义查询参数queryParams?: Record<string, any>;scroll?: {x: number;y: number;};// 批量删除配置batchDeleteConfig?: {enableBatchDelete: boolean;batchUrl: string;confirmMessage?: string;};deleteConfig?: {//删除的配置enableDelete: boolean;// 删除单个singleUrl: string;// 确认消息confirmMessage?: string;// 主键字段名,默认'id'keyField: string;};// 导出为excel配置exportExcel?: {//删除的配置enableExcel: boolean;// 确认消息confirmMessage?: string;// 主键字段名,默认'id'keyField: string;header: string[];};// 点击编辑的配置editConfig?: {enableEdit: boolean;editUrl: string;keyField: string;confirmMessage?: string;columns: any;};
// 添加的配置addConfig?: {enableAdd: boolean;addUrl: string;confirmMessage?: string;columns: any;};
}
他主要是由基本配置、批量删除配置、删除配置、导出为excel配置、编辑配置以及添加配置组成
基本配置:
获取数据的url、columns、rowSelection(可以进行选择)、rowKey、queryParams(请求参数)、scroll
url: string; // 请求数据的URLcolumns: TableProps<T>["columns"]; // 表格列配置requestParams?: Record<string, any>; // 请求参数rowSelection?: boolean; // 是否启用行选择rowKey?: string; // 行唯一标识queryParams?: Record<string, any>; // 自定义查询参数scroll?: { // 滚动配置x: number;y: number;};
其他的增删改查配置都差不多,大概的结构:
enable: boolean;Url: string;confirmMessage?: string;
如果是需要关键词查找的要加上:
keyField: string;
导出为excel的要提供header数组:
exportExcel?: {//删除的配置enableExcel: boolean;// 确认消息confirmMessage?: string;// 主键字段名,默认'id'keyField: string;header: string[];};
编辑的要提供columns:在点击编辑的时候会弹出一个弹窗,弹窗的内容是按照这里面的内容进行渲染的,具体渲染规则我在后面会写到
columns: any;
添加也是如此
(2)表格组件属性类型
规范表格组件的属性结构,进行了表格属性类型的配置,主要包括表格配置项、可选的行点击事件处理函数以及选择变更事件处理函数
interface EnhancedTableProps<T> {tableConfig: TableConfig<T>;onRowClick?: (record: T) => void;onSelectChange?: (selectedRowKeys: React.Key[], selectedRows: T[]) => void;
}
(3)组件引用类型
对于提供出去的方法,比如 handelBatchDelete、handelAdd进行声明
interface EnhancedTableRef {handelBatchDelete?: () => void;handelAdd?: () => void;
}
2、状态管理部分
const [dataSource, setDataSource] = useState<any[]>([]); // 表格数据源
const [loading, setLoading] = useState<boolean>(false); // 加载状态
const [page, setPage] = useState<number>(1); // 当前页码
const [pageSize, setPageSize] = useState<number>(10); // 每页条数
const [total, setTotal] = useState<number>(0); // 数据总量
const [isModelOpen, setIsModelOpen] = useState(false); // 模态框显示状态
const [title, setTitle] = useState(""); // 模态框标题
const [currentRecord, setCurrentRecord] = useState<any>({}); // 当前操作的记录
const [operationType, setOperationType] = useState<'add' | 'edit'>('add'); // 操作类型
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); // 选中的行 keys
const [selectedRows, setSelectedRows] = useState<[]>([]); // 选中的行数据
3、核心功能实现
(1)数据加载
逻辑:根据配置项中的url、请求参数以及page、pageSize进行请求
const loadData = async () => {setLoading(true);const { data } = await getList(tableConfig.url, {...tableConfig.queryParams,page,pageSize,});setDataSource(data.data.list);setTotal(data.data.total);setLoading(false);};
这里面有一个公共的请求数据函数:
export const getList = async (url : string , data : SearchType) => {const fullUrl = new URL(url, API_BASE_URL).href;return post(fullUrl, data );
};
(2)分页处理
const pagiNationChange = (page: number, pageSize: number) => {setPage(page);setPageSize(pageSize);};
(3)行选择配置
主要目的:
- 实现了表格行选择功能的封装,支持单选或多选
- 维护了组件内部的选择状态( selectedRowKeys 和 selectedRows )
- 提供了与父组件的通信机制,让父组件能够响应选择状态的变化
// 定义行选择配置const rowSelection = tableConfig.rowSelection? {selectedRowKeys,//绑定当前选中行的键数组(来自组件状态)onChange: (keys: React.Key[], rows: any[]) => {setSelectedRowKeys(keys);setSelectedRows(rows as []);const { onSelectChange } = restProps as EnhancedTableProps<any>;onSelectChange?.(keys, rows);},}: undefined;
选择变化函数(onChange):接收选中行的键数组、选中行的数据数组然后更新组件内部状态,最后通知父组件
通知父组件主要是通过restProps中的onSelectChange,进行设置
4、扩展功能的实现
(1)删除功能
逻辑:获取关键词进行请求
const handelDeleteItem = async (id: string) => {if (!tableConfig.deleteConfig?.enableDelete) {return;}const keyField = tableConfig.deleteConfig.keyField || "id";//获取关键词setLoading(true);try {await deleteItem(tableConfig.deleteConfig.singleUrl, keyField, id);loadData();message.success("删除成功");} catch (error) {message.error("删除失败");} finally {setLoading(false);}};
删除也是写了一个公共的删除函数的
export const deleteItem = async (url : string , keyField : string , data : any) => {const fullUrl = new URL(url, API_BASE_URL).href;return post(fullUrl, {[keyField] : data});
}
这里注意一下 {[keyField] : data}使用键值对的方式
(2)编辑&&添加功能
逻辑:都是打开弹窗->填写弹窗的内容
弹窗也在后面进行说明
编辑:打开弹窗->设置弹窗标题->将当前要编辑的记录存入状态,供弹窗内部使用->设置当前操作类型为 “edit”(编辑),用于区分新增、编辑等不同操作
const handelEdit = (record?: any) => {if (!tableConfig.editConfig?.enableEdit) {return;}setIsModelOpen(true);setTitle(tableConfig.editConfig?.confirmMessage || "编辑");setCurrentRecord(record);setOperationType('edit');};
添加:同上
const handelAdd = () => {if (!tableConfig.addConfig?.enableAdd) {return;}setIsModelOpen(true);setTitle(tableConfig.addConfig?.confirmMessage || "添加");setCurrentRecord({});setOperationType('add');};
(3)批量删除
逻辑:传入url和 selectedRowKeys进行批量删除
// 批量删除的方法:const handelBatchDelete = async () => {if (!tableConfig.batchDeleteConfig?.enableBatchDelete) {return;}setLoading(true);try {await deleteItems(tableConfig.batchDeleteConfig.batchUrl,selectedRowKeys);loadData();message.success("删除成功");} catch (error) {message.error("删除失败");} finally {setLoading(false);}};
5、组件通信
通过 useImperativeHandle 暴露方法给父组件:
React.useImperativeHandle(ref, () => ({handelBatchDelete,handelAdd,
}));
6、生命周期与副作用函数
加载数据
// 初始加载数据
useEffect(() => {loadData();
}, [page, pageSize, tableConfig.queryParams]);
动态配置操作项
operateColumn.render = (value, record) => {return (<>{tableConfig.deleteConfig?.enableDelete && (<RenderOperateButtonstype="primary"size="small"dangerclassName="ml mr mb"confirmMessage={tableConfig.deleteConfig?.confirmMessage}option={() => {const keyField =tableConfig.deleteConfig?.keyField || "id";handleDeleteItem((record as any)[keyField]);}}/>)}{tableConfig.exportExcel?.enableExcel && (<RenderOperateButtonstype="primary"size="small"className="ml mr"confirmMessage={tableConfig.exportExcel?.confirmMessage}option={() => {const header = tableConfig.exportExcel?.header || [];exportToExcel([record], header);}}/>)}{tableConfig.editConfig && (<RenderOperateButtonstype="primary"size="small"className="ml mr"confirmMessage={tableConfig.editConfig?.confirmMessage}option={() => {handleEdit(record);}}/>)}</>);};
进行动态渲染是根据每一个配置项的enable进行条件渲染的,react好像不太支持if(之前准备直接使用if,但是被警告了),所以改为了现在这种方式:
{tableConfig.editConfig && (<Button</Button>)}
首先寻找到操作列
tableConfig.columns.find((col) => col.key === "operate") :查找 key 为 "operate" 的列(即操作列)
然后进行自定义渲染,关于封装的RenderOperateButtons也留在后面进行讲解
7、UI部分
主要是弹窗、表格以及分页器
表格和分页器
<Tableloading={loading}rowKey={tableConfig.rowKey || "id"}rowSelection={rowSelection}columns={tableConfig.columns}dataSource={dataSource}scroll={tableConfig.scroll}pagination={false}/><PaginationclassName="mm mt"total={total}showSizeChangercurrent={page}pageSize={pageSize}onChange={pagiNationChange}/>
8、其他封装的组件
(1)弹窗
InputTable:主要用于创建和编辑数据时展示表单界面。
主要逻辑:通过配置化方式生成表单,支持多种表单控件类型(输入框、单选框、下拉选择器等),并提供表单验证。
接口定义:
主要是由列配置和组件属性接口两部分组成
列配置:
interface ColumnConfig {name: string; // 字段名label: string; // 标签名type: 'input' | 'radio' | 'select' | 'textarea'; // 字段类型required?: boolean; // 是否必填rules?: any[]; // 验证规则options?: { value: string; label: string }[]; // 选择类型的选项placeholder?: string; // 占位符span?: number; // 占用列数initialValue?: any; // 初始值
}
组件属性:
interface FormProps {visible: boolean;//是否显示hideMode: () => void;//隐藏弹窗title: string;//标题loadData: () => void;//加载数据的函数initialData: any;//初始数据columns: ColumnConfig[]; // 列配置onFinish: (values: any) => void; // 可选的提交回调
}
状态管理
组件使用 Ant Design 的 Form.useForm 钩子创建表单实例,用于管理表单状态和操作:
const { visible, hideMode, title, loadData, columns, initialData, onFinish } = props;const [form] = Form.useForm<any>();
生命周期和副作用
使用 useEffect 钩子处理组件的副作用,主要负责在模态框显示时初始化表单数据:
useEffect(() => {if (visible) {if (title.includes("添加")) {form.resetFields();} else if (initialData) {form.setFieldsValue(initialData);}}
}, [visible, title, initialData]);
因为涉及到多种情况要使用这个弹窗,为了解决添加没有数据,编辑的时候实现数据的回显所以使用这样比较死的方法。
处理列分组:根据传入的colmns进行渲染
// 根据列类型渲染不同的表单控件const renderFormItem = () => {switch (column.type) {case 'input':return <Input placeholder={column.placeholder || ``} />;case 'radio':return (<Radio.Groupoptions={column.options || []}/>);case 'select':return (<Selectplaceholder={column.placeholder || ``}options={column.options || []}/>);case 'textarea':return <Input.TextArea placeholder={column.placeholder || ``} />;default:return <Input placeholder={column.placeholder || ``} />;}};currentRow.push(<Col key={`col-${index}-${column.name}`} span={span}><Form.Itemlabel={column.label}name={column.name}rules={column.rules || (column.required ? [{ required: true, message: `请输入${column.label}` }] : [])}className="[&_.ant-form-item-label]>label:text-gray-600">{renderFormItem()}</Form.Item></Col>);currentSpan += span;});
表单提交函数:
const handleFinish = async (values: any) => {try {// 如果提供了自定义提交回调,则使用它if (onFinish) {onFinish(values);} else {// 否则使用默认的提交逻辑const { data } = await changeUserList(values);message.success(data.message);loadData();}hideMode();} catch (error) {message.error("提交失败");}};
UI 渲染
组件的 UI 结构主要包含一个 Ant Design 的 Modal 组件,内部嵌套 Form 组件:
<div className="inputTable"><Modalcenteredtitle={title}onCancel={hideMode}open={visible}width={50 + "vw"}footer={[<Button key="cancel" onClick={hideMode}>取消</Button>,<Button key="submit" type="primary" onClick={() => form.submit()}>确认提交</Button>,]}><Formform={form}name="dynamicForm"labelCol={{ span: 8 }}wrapperCol={{ span: 16 }}validateTrigger="onSubmit"onFinish={handleFinish}autoComplete="off">{renderColumnsInRows()}// 渲染表单列</Form></Modal></div>
(2)按钮
import { Button } from "antd"
interface ButtonConfig {type?: "link" | "text" | "default" | "primary" | "dashed" | undefined;size?: "small" | "middle" | "large";className?: string;option?:() => void;confirmMessage?:string;danger?:boolean;}export default function RenderOperateButtons(config:ButtonConfig) {return (<Buttontype={config.type}size={config.size}className={config.className}onClick={config.option}danger={config.danger || false}>{config.confirmMessage}</Button>)}
9、使用说明
EnhancedTable 是基于 Ant Design Table 组件封装的增强表格组件,提供了数据加载、分页、行选择、删除、编辑、添加等一体化功能,支持配置化使用,减少重复代码编写。
(1)组件属性
属性名 | 类型 | 说明 | 必填 |
---|---|---|---|
tableConfig | TableConfig | 表格配置对象 | 是 |
onRowClick | (record: T) => void | 行点击事件处理函数 | 否 |
onSelectChange | (selectedRowKeys: React.Key[], selectedRows: T[]) => void | 选择变更事件处理函数 | 否 |
(2)填写TableConfig
interface TableConfig {url: string;columns: TableProps<DataType>["columns"];
}
const tableConfig = useMemo<TableConfig>(() => {return {url: ",rowKey: "",queryParams: ,columns,deleteConfig:{enableDelete: true,confirmMessage: "删除",keyField: "",singleUrl: "",},editConfig:{enableEdit: true,confirmMessage: "",keyField: "",editUrl: "",columns},......};}, [formData]); // 添加formData到依赖数组
(3)批量删除&&添加(如有需要)
<EnhancedTableonSelectChange={(keys, rows) => {setSelectedRowKeys(keys);setSelectedRows(rows);}}//父子通信选中的行tableConfig={tableConfig}ref={tableRef}//通过ref可以使用到子提供的函数/>
状态管理:
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);const [selectedRows, setSelectedRows] = useState<DataType[]>([]);
定义子提供的函数的ref类型
const tableRef = useRef<{ handleBatchDelete: () => void; handleAdd: () => void }>(null);
定义点击批量删除/添加按钮的函数
// 调用批量删除const handelBatchDelete = () => {tableRef.current?.handleBatchDelete();};// 调用新增const handelAdd = () => {tableRef.current?.handleAdd();};
使用
<Button type="primary" onClick={handelAdd}>新增企业</Button><Buttontype="primary"dangerclassName="delete ml"disabled={selectedRowKeys.length ? false : true}onClick={handelBatchDelete}// onClick={handelDelete}>批量删除</Button>
(4)高级功能
表单列配置
interface ColumnConfig {name: string; // 字段名label: string; // 标签名type: 'input' | 'radio' | 'select' | 'textarea'; // 字段类型required?: boolean; // 是否必填rules?: any[]; // 验证规则options?: { value: string; label: string }[]; // 选择类型的选项placeholder?: string; // 占位符span?: number; // 占用列数initialValue?: any; // 初始值
}
行选择功能:
通过设置 rowSelection: true 启用行选择功能,支持单选和多选,可通过 onSelectChange 事件获取选中的行。