react加antd封装表格单、多选组件,支持跨页选择缓存

页面效果

子组件

复制代码
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;
相关推荐
悬炫2 小时前
深入解析浏览器渲染原理与性能优化策略
前端·javascript
铠文2 小时前
垃圾回收机制核心知识点
javascript
谦谦橘子2 小时前
rxjs原理解析
前端·javascript·函数式编程
竺梓君2 小时前
移动端开发常见问题及解决方案
前端·javascript
getapi2 小时前
Express.js 是一个轻量级、灵活且功能强大的 Node.js Web 应用框架
前端·javascript·express
yqcoder2 小时前
Redis 的特点
数据库·redis·缓存
江沉晚呤时2 小时前
C# 建造者模式(Builder Pattern)详细讲解
java·开发语言·javascript·数据库·c#·.netcore·net
shadouqi2 小时前
2.angular指令
前端·javascript·angular.js
ONLYOFFICE2 小时前
如何使用 ONLYOFFICE 宏对 PDF 表单中的特定字段执行计算
java·javascript·pdf
Shawn_LX3 小时前
Vue3 + Vite + Yarn + Fabricjs构建的开源演示系统
javascript·typescript·vue·html5·fabric