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 小时前
分布式缓存-GO(分布式算法之一致性哈希、缓存对外服务化)
开发语言·经验分享·分布式·后端·算法·缓存·golang
哆啦A梦15883 小时前
商城后台管理系统 03 登录布局
javascript·vue.js·elementui
曼巴UE53 小时前
UE FString, FName ,FText 三者转换,再次学习,官方文档理解
服务器·前端·javascript
code bean4 小时前
【CMake】为什么需要清理 CMake 缓存文件?深入理解 CMake 生成器切换机制
java·spring·缓存
selt7914 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
行走的陀螺仪4 小时前
高级前端 Input 公共组件设计方案(Vue3 + TypeScript)
前端·javascript·typescript·vue·组件设计方案
一颗不甘坠落的流星5 小时前
【Antd】基于 Upload 组件,导入Json文件并转换为Json数据
前端·javascript·json
LYFlied5 小时前
Vue2 与 Vue3 虚拟DOM更新原理深度解析
前端·javascript·vue.js·虚拟dom
小飞侠在吗5 小时前
vue shallowRef 与 shallowReacitive
前端·javascript·vue.js
武子康5 小时前
Java-193 Spymemcached 深入解析:线程模型、Sharding 与序列化实践全拆解
java·开发语言·redis·缓存·系统架构·memcached·guava