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;
相关推荐
LCG元6 小时前
STM32实战:基于STM32F103的Bootloader设计与IAP在线升级
javascript·stm32·嵌入式硬件
前端一小卒7 小时前
前端工程师的全栈焦虑,我用 60 天治好了
前端·javascript·后端
ward RINL8 小时前
redis分页查询
数据库·redis·缓存
oLLI PILO8 小时前
Redis连接池
数据库·redis·缓存
coderyi9 小时前
LLM Agent 浅析
前端·javascript·人工智能
我叫黑大帅9 小时前
TypeScript 6.0 弃用选项错误 TS5101 解决方法
javascript·后端·面试
科雷软件测试9 小时前
使用python+Midscene.js AI驱动打造企业级WEB自动化解决方案
前端·javascript·python
热爱Java,热爱生活9 小时前
浅谈Spring三级缓存
java·spring·缓存
heRs BART9 小时前
Redis简介、常用命令及优化
数据库·redis·缓存
We་ct9 小时前
LeetCode 120. 三角形最小路径和:动态规划详解
前端·javascript·算法·leetcode·typescript·动态规划