页面效果
子组件
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
import { Modal, Input, Table, Pagination, Avatar, Select } from 'antd';
import { UserOutlined } from '@ant-design/icons';
import type { TableProps, PaginationProps } from 'antd';
// 用户接口
interface User {
id: string;
name: string;
avatar: string;
}
// 组件属性接口
interface UserSelectorProps<T> {
open: boolean;
mode: 'single' | 'multiple';
dataSource: T[];
total: number;
onCancel: () => void;
onOk: (selectedItems: T[]) => void;
onSearch: (searchText: string) => void;
onPageChange: (page: number, pageSize: number) => void;
}
// 暴露给父组件的接口
export interface UserSelectorRef<T> {
setSelectedItems: (items: T[]) => void;
}
// 主函数
const UserSelector = forwardRef<UserSelectorRef<User>, UserSelectorProps<User>>(
(
{
open,
onCancel,
onOk,
mode,
dataSource,
total,
onSearch,
onPageChange,
},
ref,
) => {
当前选中项
const [selectedItems, setSelectedItems] = useState<User[]>([]);
// 暴露方法给父组件
useImperativeHandle(ref, () => ({
setSelectedItems: (items: User[]) => {
setSelectedItems(items);
},
}));
/ 搜索框
const [searchText, setSearchText] = useState('');
// 处理搜索框变化
const handleSearch = (value: string) => {
setSearchText(value);
onSearch(value);
};
对话框关闭时重置选中和搜索框
useEffect(() => {
if (!open) {
setSelectedItems([]);
setSearchText('');
}
}, [open]);
分页
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(5);
// 分页改变
const handlePageChange: PaginationProps['onChange'] = (page, pageSize) => {
setCurrentPage(page);
setPageSize(pageSize);
onPageChange(page, pageSize);
};
/ 表格
// 列配置对象
const columns: TableProps<User>['columns'] = [
{
key: 'avatar',
render: (_, record) => (
<div style={{ display: 'flex', alignItems: 'center' }}>
{record.avatar ? (<Avatar
src={record.avatar}
/>) : (<Avatar
icon={<UserOutlined />} // 如果头像为空,显示默认图标
/>)}
<span style={{ marginLeft: 8 }}>{record.name}</span>
</div>
),
},
];
// 表格行点击事件
const handleRowClick = (record: User) => {
if (mode === 'single') {
onOk([record]);
onCancel()
} else {
// 判断该行是否已经选择
const isSelected = selectedItems.some(item => item.id === record.id);
if (isSelected) {
// 已选择,进行删除操作
setSelectedItems(selectedItems.filter(item => item.id !== record.id));
} else {
// 未选择,进行添加
setSelectedItems([...selectedItems, record]);
}
}
};
// 表格全选,全取消
const handleSelectAll = (selected: boolean) => {
if (selected) {
// 全选时,将当前页未选择的用户追加到已选用户列表
const newSelectedItems = [
...selectedItems,
...dataSource.filter(
item => !selectedItems.some(selectedItem => selectedItem.id === item.id),
),
];
setSelectedItems(newSelectedItems);
} else {
// 取消全选时,移除当前页的用户
const newSelectedItems = selectedItems.filter(
item => !dataSource.some(row => row.id === item.id),
);
setSelectedItems(newSelectedItems);
}
};
多选框
// 监听多选框的改变
const handleSelectChange = (values: string[]) => {
const newSelectedItems = selectedItems.filter(item => values.includes(item.id));
setSelectedItems(newSelectedItems);
};
return (
<Modal
title="Select"
open={open}
onCancel={onCancel}
onOk={() => onOk(selectedItems)}
width={600}
>
<Input.Search
size="large"
allowClear
placeholder="search"
value={searchText}
onChange={e => handleSearch(e.target.value)}
style={{ marginBottom: 16 }}
/>
{mode === 'multiple' && (
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 16 }}>
<Select
size="large"
mode="multiple"
allowClear
value={selectedItems.map(item => item.id)}
onChange={handleSelectChange}
style={{ width: '100%' }}
placeholder="select"
optionLabelProp="label"
options={selectedItems.map(item => ({
value: item.id,
label: (
<div style={{ display: 'flex', alignItems: 'center', }}>
{item.avatar ? (<Avatar
src={item.avatar}
/>) : (<Avatar
icon={<UserOutlined />} // 如果头像为空,显示默认图标
/>)}
{item.name}
</div>
),
}))}
/>
</div>
)}
<Table
dataSource={dataSource}
columns={columns}
rowKey="id"
pagination={false}
onRow={record => ({
onClick: () => handleRowClick(record),
})}
rowSelection={
mode === 'multiple'
? {
selectedRowKeys: selectedItems.map(item => item.id),
onSelect: (record, selected, selectedRows, nativeEvent) => {
handleRowClick(record)
},
onSelectAll: (selected, selectedRows, changeRows) => {
handleSelectAll(selected);
},
}
: undefined
}
/>
<Pagination
{
...{
pageSize: pageSize,
current: currentPage,
showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} items`,
showSizeChanger: true,
pageSizeOptions: ['5', '10', '20', '30'],
onChange: handlePageChange,
total: total
}
}
style={{ marginTop: 16 }}
/>
</Modal>
);
},
);
export default UserSelector;
父组件
import React, { useState, useRef } from 'react';
import UserSelector, { UserSelectorRef } from './UserSelector2';
import { Button, Avatar } from 'antd';
interface User {
id: string;
name: string;
avatar: string;
}
const App: React.FC = () => {
const [open, setOpen] = useState(false);
const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
const userSelectorRef = useRef<UserSelectorRef<User>>(null);
const [mockUsers, setMockUsers] = useState<User[]>([
{ id: '1', name: '用户1', avatar: '1' },
{ id: '2', name: '用户2', avatar: 'https://example.com/avatar2.png' },
{ id: '3', name: '用户3', avatar: '1' },
{ id: '4', name: '用户4', avatar: 'https://example.com/avatar2.png' },
{ id: '5', name: '用户5', avatar: '' },
]);
const handleSearch = (searchText: string) => {
console.log('Search:', searchText);
};
const handlePageChange = (page: number, pageSize: number) => {
if (page === 1) {
setMockUsers([
{ id: '1', name: '用户1', avatar: '1' },
{ id: '2', name: '用户2', avatar: 'https://example.com/avatar2.png' },
{ id: '3', name: '用户3', avatar: '1' },
{ id: '4', name: '用户4', avatar: 'https://example.com/avatar2.png' },
{ id: '5', name: '用户5', avatar: '1' },
])
} else {
setMockUsers([
{ id: '6', name: '用户6', avatar: 'https://example.com/avatar2.png' },
{ id: '7', name: '用户7', avatar: '1' },
{ id: '8', name: '用户8', avatar: 'https://example.com/avatar2.png' },
{ id: '9', name: '用户9', avatar: '1' },
{ id: '10', name: '用户10', avatar: 'https://example.com/avatar2.png' },
])
}
};
const handleSetSelectedUsers = () => {
// 父组件调用子组件的方法,设置选中用户
if (userSelectorRef.current) {
userSelectorRef.current.setSelectedItems([
{ id: '1', name: '用户1', avatar: 'https://example.com/avatar1.png' },
]);
}
};
return (
<div>
<Button type="primary" onClick={() => setOpen(true)} style={{ marginRight: 8 }}>
选择用户
</Button>
<Button onClick={handleSetSelectedUsers}>设置选中用户</Button>
<UserSelector
ref={userSelectorRef}
open={open} // 使用 open 替代 visible
onCancel={() => setOpen(false)}
onOk={setSelectedUsers}
mode="multiple"
dataSource={mockUsers}
total={10}
onSearch={handleSearch}
onPageChange={handlePageChange}
/>
<div>
<h3>已选用户:</h3>
{selectedUsers.map(user => (
<div key={user.id}>
<Avatar src={user.avatar} />
<span>{user.name}</span>
</div>
))}
</div>
</div>
);
};
export default App;