今天我们继续讲解Ant Design中的Table
组件,今天的主题是如何为Table
组件添加操作功能,这里的操作是针对每一行数据而言的,比如我们可以编辑、删除某一行数据,或者查看某一行数据的详情等。
要实现这些操作,我们需要在Table
组件的列定义中添加一个操作列(操作列通常位于最后一列),这个操作列通常会包含一些按钮或者菜单,用户可以通过点击这些按钮或菜单来执行相应的操作。
因为操作列并不是数据(Table组件中的dataSource)的一部分,所以这一列并不需要dataIndex
属性,我们可以直接在columns
数组的末尾添加一个新的列对象,设置其title
为“操作”,并使用render
渲染具体的操作内容,比如编辑和删除按钮。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const columns = [ { title: '操作', key: 'action', render: (_: any, record: User) => ( <span> <a onClick={() => console.log('Edit', record)}>编辑</a> <span style={{ margin: '0 8px' }}>|</span> <a onClick={() => console.log('Delete', record)}>删除</a> </span> ), }, ];
|
有的时候,因为render函数对应的代码比较多,我们可以单独将其提取出来,作为一个单独的函数来处理,这样可以使代码更清晰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const renderActions = (record: User) => ( <span> <a onClick={() => console.log('Edit', record)}>编辑</a> <span style={{ margin: '0 8px' }}>|</span> <a onClick={() => console.log('Delete', record)}>删除</a> </span> );
const columns = [ { title: '操作', key: 'action', render: renderActions, }, ];
|
我们向render
函数传入了当前行的数据(record),这样我们就可以在操作中使用这行数据了,上面例子中,我们只是简单地打印了编辑和删除的记录,但在实际应用中,我们可能会调用后台api来执行编辑或删除操作。
由于已经拿到了当前行数据record
,所以我们也可以拿到这行数据对应的id
,有了id
,我们就可以在点击编辑或删除按钮时,将id
传递给相应的后台API来做真实的编辑和删除操作了。
最后,我们看一下render
函数的定义,它接收三个参数,分别是:
value
: 当前单元格的值
record
: 当前行的数据记录
index
: 当前行的索引
在我们的例子中,我们并没有使用value
和index
,但它们在某些情况下可能会有用,比如当你需要根据行索引来处理某些逻辑时。
1
| render?: (value: any, record: RecordType, index: number) => React.ReactNode | RenderedCell<RecordType>;
|
render
函数的返回值可以是一个React节点(比如一个按钮或链接),也可以是一个自定义的渲染组件。目前我们用到的都是第一种情况,返回一个React节点。
下面我们以编辑和删除为例,看看如何通过Actions列处理这两种操作。
定义Action列
首先,我们在Action列中定义两个按钮:编辑和删除,当用户点击这两个按钮时,分别执行对应的操作。为了显示美观,我们使用Space
组件来排列这两个按钮。Space组件会自动在两个组件之间加上间距。
1 2 3 4 5 6 7 8
| const renderAction = (value: any, record: User) => { return ( <Space> <Button type='link' onClick={() => onEditButtonClick(record)}>编辑</Button> <Button type='link' onClick={() => onDeleteButtonClick(record)}>删除</Button> </Space> ) }
|
删除操作
这里使用onDeleteButtonClick
函数来处理删除操作。删除操作是一个比较敏感的操作,通常来说,在执行删除操作之前,我们需要弹出一个确认对话框,询问用户是否真的要删除这个记录。也就是要给用户一个二次确认的机会。这里我们使用Ant Design的Modal
组件来实现这个功能。
1 2 3 4 5 6 7 8 9 10 11 12
| const onDeleteButtonClick = (record: User) => { Modal.confirm({ title: '确认删除', content: `您确定要删除用户 ${record.name} 吗?`, onOk: () => { handleDelete(record.id); }, onCancel: () => { console.log('Delete cancelled'); }, }) }
|
只有当用户点击了Modal上的“删除”按钮时,才会真正执行删除操作。
调用API删除数据
1 2 3 4 5 6 7 8 9 10
| const handleDelete = async (userId: number) => { try { await deleteUser(userId); getUsers(pageNumber, pageSize); } catch (error) { console.error('Error deleting user:', error); } }
|
删除的操作就比较简单了,我们调用后端API deleteUser,传入用户的ID,然后在删除成功后重新获取用户列表。
编辑操作
编辑操作一般通过弹窗的形式来实现,在Ant Design中,弹窗就是Modal组件。我们可以在点击编辑按钮时,弹出一个Modal,里面包含一个表单,用户可以在这个表单中修改用户信息。
定义编辑Modal
首先定义这个Modal的属性,注意:Modal的打开和关闭需要父控件来控制,所以我们传入一个变量isVisible
来控制Modal的显示和隐藏。另外,我们还需要传入一个userId
来表示当前编辑的用户ID(用来获取用户详情并显示在Modal上),以及两个回调函数onOK
和onCancel
,分别在编辑成功或者取消编辑时调用。
1 2 3 4 5 6
| type UserEditModalProps = { isVisible: boolean; userId: number | null; onOK: () => void; onCancel: () => void; }
|
紧接着定义Modal组件,组件的命名规则一般是:业务名称 + 操作名称 + 组件类型,所以我们这里就命名为UserEditModal
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| export default function UserEditModal(props: UserEditModalProps) { const [form] = Form.useForm(); const { isVisible, userId, onOK, onCancel } = props; return ( <Modal title="编辑用户" open={isVisible} okText={'保存'} onOk={onOkButtonClick} onCancel={onCancel} cancelText={'取消'} > <Form form={form} layout="vertical"> <Form.Item name="name" label="姓名" rules={[{ required: true, message: '请输入姓名' }]} > <Input placeholder="请输入姓名" /> </Form.Item> <Form.Item name="email" label="邮箱" rules={[{ required: true, message: '请输入邮箱' }]} > <Input placeholder="请输入邮箱" /> </Form.Item> {/* 可以添加更多表单项 */} </Form> </Modal> ) }
|
因为我们这里使用了Form
组件,所以需要先创建一个form
实例,这个实例会被传递给Modal中的Form组件。接下来,我们需要在Modal中添加表单项,比如姓名和邮箱等。
接下来我们通过传入的userId
来获取用户详情,并将其设置到表单中,这样用户在编辑时就可以看到当前用户的信息了。
1 2 3 4 5 6
| useEffect(() => { if (isVisible && userId) { getUserInfo(userId); } }, [isVisible, userId]);
|
这里需要注意的是,Ant Design的Modal组件无论打开还是关闭,都会渲染到页面上,为了避免在Modal打开之前就调用API,我们这里加了isVisible
的判断,只有当Modal是可见的,并且userId
存在时,才会调用getUserInfo
函数来获取用户信息。
当成功获取用户信息后,通过form.setFieldsValue
将这些信息显示到页面上。
1 2 3 4
| const getUserInfo = async (userId: number) => { const userDetail = await getUserDetail(userId); form.setFieldsValue(userDetail); }
|
接下来就是编辑操作了,当用户点击Modal的保存
按钮时,我们需要验证表单数据是否有效,如果有效,就调用API来保存用户信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const submitForm = async (values: any) => { const res = await submitUser(values); if (res.success) { onOK(); } else { console.error('保存失败:', res.message); } }
const onOkButtonClick = () => { form.validateFields().then(values => { submitForm(values) }).catch(error => { console.error('表单验证失败:', error); }); }
|
如果表单数据验证失败,比如某些必填项没有填写,或者格式不正确,validateFields
会抛出一个错误,Ant Design也会在页面上给出相应的提示。
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| export default function UserEditModal(props: UserEditModalProps) { const [form] = Form.useForm(); const { isVisible, userId, onOK, onCancel } = props;
const getUserInfo = async (userId: number) => { const userDetail = await getUserDetail(userId); form.setFieldsValue(userDetail); }
const submitForm = async (values: any) => { const res = await submitUser(values); if (res.success) { onOK(); } else { console.error('保存失败:', res.message); } }
const onOkButtonClick = () => { form.validateFields().then(values => { submitForm(values) }) }
useEffect(() => { if (isVisible && userId) { getUserInfo(userId); } }, [isVisible, userId]);
return ( <Modal title="编辑用户" open={isVisible} okText={'保存'} onOk={onOkButtonClick} onCancel={onCancel} cancelText={'取消'} > <Form form={form} layout="vertical"> <Form.Item name="name" label="姓名" rules={[{ required: true, message: '请输入姓名' }]} > <Input placeholder="请输入姓名" /> </Form.Item> <Form.Item name="email" label="邮箱" rules={[{ required: true, message: '请输入邮箱' }]} > <Input placeholder="请输入邮箱" /> </Form.Item> </Form> </Modal> ) }
|
好了,今天就到这里了,我们明天再见!