当前位置: 首页 > article >正文

React封装通用Table组件,支持搜索(多条件)、筛选、自动序号、数据量统计等功能。未采用二次封装调整灵活,包含使用文档

封装通用组件

  • 一、封装思想
  • 二、react代码
  • 三、css代码
  • 四、实现效果
  • 五、使用文档 BasicTableModal 表格模态框组件
    • 1.组件简介
    • 2.功能特点
    • 3.使用方法
      • 基础用法
      • 宽度控制示例
      • 带筛选功能
      • 搜索功能示例
      • 自定义单元格渲染
    • 4.API 说明
      • Props
      • Column 配置项
      • Filter 配置项
    • 5.注意事项

一、封装思想

1.通用性:可以适用于多种表格展示场景,样式设计更灵活
2.可配置性:提供丰富的配置选项
3.易用性:使用方式简单直观
4.可维护性:代码结构清晰,逻辑分明
5.可扩展性:预留了自定义渲染等扩展接口

二、react代码

import React from 'react';
import PropTypes from 'prop-types'; // 通过PropTypes实现数据校验
import 'src/css/basicTableModal.css';

class BasicTableModal extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            searchText: '',
            filterValues: {},
            filteredData: props.data || [],
            visible: props.visible || false
        };
    }

    // 添加序号列到columns中
    getColumnsWithIndex = () => {
        const indexColumn = {
            key: '_index',
            title: '序号',
            render: (text, record, index) => index + 1
        };
        return [indexColumn, ...this.props.columns];
    }

    componentDidMount() {
        this.filterAndSearchData();
    }

    componentDidUpdate(prevProps) {
        if (prevProps.data !== this.props.data) {
            this.filterAndSearchData();
        }
        if (prevProps.visible !== this.props.visible) {
            this.setState({ visible: this.props.visible });
        }
    }

    // 处理搜索和筛选
    filterAndSearchData = () => {
        const { data, searchableKeys = [] } = this.props;
        const { searchText, filterValues } = this.state;
        
        let result = [...data];

        // 处理搜索
        if (searchText.trim()) {
            result = result.filter(item => {
                return searchableKeys.some(key => {
                    const cellValue = item[key];
                    return cellValue && 
                        String(cellValue)
                            .toLowerCase()
                            .includes(searchText.toLowerCase());
                });
            });
        }

        // 处理筛选
        Object.entries(filterValues).forEach(([key, value]) => {
            if (value) {
                result = result.filter(item => item[key] === value);
            }
        });

        this.setState({ filteredData: result });
    }

    handleSearchChange = (e) => {
        this.setState({ searchText: e.target.value }, this.filterAndSearchData);
    }

    handleFilterChange = (key, value) => {
        this.setState(prevState => ({
            filterValues: {
                ...prevState.filterValues,
                [key]: value
            }
        }), this.filterAndSearchData);
    }

    handleClose = () => {
        const { onClose } = this.props;
        this.setState({ visible: false });
        if (onClose) {
            onClose();
        }
    }

    // 渲染筛选下拉框
    renderFilterDropdown = (column) => {
        if (!column.filters) return null;

        return (
            <select
                className="filter-select"
                value={this.state.filterValues[column.key] || ''}
                onChange={(e) => this.handleFilterChange(column.key, e.target.value)}
            >
                <option value="">全部</option>
                {column.filters.map((filter, index) => (
                    <option key={index} value={filter.value}>
                        {filter.text}
                    </option>
                ))}
            </select>
        );
    }

    render() {
        const { title, searchPlaceholder = "输入关键字搜索...", width } = this.props;
        const { searchText, filteredData, visible } = this.state;
        const columnsWithIndex = this.getColumnsWithIndex();

        if (!visible) return null;

        const modalStyle = {
            width: width || '80%',
            maxWidth: '1000px'
        };

        return (
            <div className="modal-overlay">
                <div className="modal-content" style={modalStyle}>
                    <div className="modal-header">
                        <h3>{title || '表格详情'}</h3>
                        <button className="close-button" onClick={this.handleClose}>×</button>
                    </div>
                    <div className="basic-table-container">
                        <div className="table-toolbar">
                            {(this.props.searchableKeys && this.props.searchableKeys.length > 0) && (
                                <input
                                    type="text"
                                    placeholder={searchPlaceholder}
                                    value={searchText}
                                    onChange={this.handleSearchChange}
                                    className="search-input"
                                />
                            )}
                            <div className="filter-container">
                                {this.props.columns.map(column => (
                                    column.filters && (
                                        <div key={column.key} className="filter-item">
                                            <span className="filter-label">{column.title}:</span>
                                            {this.renderFilterDropdown(column)}
                                        </div>
                                    )
                                ))}
                            </div>
                        </div>
                        <div className="table-content-wrapper">
                            <table className="basic-table">
                                <thead>
                                    <tr>
                                        {columnsWithIndex.map(column => (
                                            <th key={column.key} style={{ width: column.width || 'auto' }}>{column.title}</th>
                                        ))}
                                    </tr>
                                </thead>
                                <tbody>
                                    {filteredData.map((item, index) => (
                                        <tr key={index} className={index % 2 === 0 ? 'table-row-light' : 'table-row-dark'}>
                                            {columnsWithIndex.map(column => (
                                                <td key={column.key} style={{ width: column.width || 'auto' }}>
                                                    {column.render 
                                                        ? column.render(item[column.key], item, index) 
                                                        : item[column.key]}
                                                </td>
                                            ))}
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        </div>
                        <div className="table-footer">
                            <span className="data-count">数量({filteredData.length})</span>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

// 数据格式校验
BasicTableModal.propTypes = {
    columns: PropTypes.arrayOf(PropTypes.shape({
        key: PropTypes.string.isRequired,
        title: PropTypes.string.isRequired,
        render: PropTypes.func,
        width: PropTypes.oneOfType([
            PropTypes.number,
            PropTypes.string
        ]),
        filters: PropTypes.arrayOf(PropTypes.shape({
            text: PropTypes.string.isRequired,
            value: PropTypes.any.isRequired
        }))
    })).isRequired,
    data: PropTypes.array.isRequired,
    visible: PropTypes.bool,
    onClose: PropTypes.func,
    title: PropTypes.string,
    searchableKeys: PropTypes.arrayOf(PropTypes.string),
    searchPlaceholder: PropTypes.string,
    width: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string
    ])
};

BasicTableModal.defaultProps = {
    data: [],
    visible: false,
    searchableKeys: [],
    width: '80%'
};

export default BasicTableModal;

三、css代码

/* 模态框基础样式
-------------------------------------------------- */
.modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.5);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 1000;
}

.modal-content {
    background-color: white;
    border-radius: 4px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    max-height: 90vh;
    display: flex;
    flex-direction: column;
    position: relative;
}

/* 模态框头部样式
-------------------------------------------------- */
.modal-header {
    background-color: #eaeaea;
    border-bottom: 1px solid #e8e8e8;
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: relative;
    border-radius: 4px 4px 0 0;
}

.modal-header::before {
    content: '';
    position: absolute;
    top: 9px;
    left: 10px;
    height: 14px;
    border-left: 2px solid #f95e34;
}

.modal-header h3 {
    margin: 0;
    font-size: 12px;
    font-family: Microsoft Yahei;
    color: rgba(0, 0, 0, 0.85);
    margin-left: 15px;
}

.close-button {
    background: none !important;
    cursor: pointer;
    line-height: 33px;
}

/* 表格容器布局
-------------------------------------------------- */
.basic-table-container {
    padding: 5px;
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    position: relative;
}

.table-content-wrapper {
    overflow: auto;
    flex: 1;
    min-height: 0;
}

/* 表格基础样式
-------------------------------------------------- */
.basic-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    background-color: #fff;
    font-size: 12px;
    font-family: Microsoft Yahei;
    line-height: 0.5;
}

.basic-table th,
.basic-table td {
    padding: 12px 8px;
    border: 1px solid #e8e8e8;
    text-align: left;
}

/* 表格头部样式
-------------------------------------------------- */
.basic-table thead {
    position: sticky;
    top: 0;
    z-index: 2;
    background-color: #fafafa;
}

.basic-table th {
    background-color: #fafafa;
    font-weight: 500;
    box-shadow: 0 1px 0 #e8e8e8;
}

/* 表格内容样式
-------------------------------------------------- */
.basic-table tbody {
    overflow-y: auto;
}

.table-row-light {
    background-color: #f0f0f0;
}

.table-row-dark {
    background-color: #ffffff;
}

/* 滚动条样式
-------------------------------------------------- */
.basic-table-container::-webkit-scrollbar {
    display: none;
}

.table-content-wrapper::-webkit-scrollbar {
    width: 4px;
    height: 8px;
}

.table-content-wrapper::-webkit-scrollbar-thumb {
    background-color: #c1c1c1;
    border-radius: 20px;
    transition: background-color 0.3s;
}

.table-content-wrapper::-webkit-scrollbar-thumb:hover {
    background-color: #a8a8a8;
}

.table-content-wrapper::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 20px;
}

.table-content-wrapper::-webkit-scrollbar-corner {
    background: transparent;
}

/* 工具栏样式
-------------------------------------------------- */
.table-toolbar {
    display: flex;
    align-items: center;
    padding: 8px;
    gap: 16px;
    position: sticky;
    top: 0;
    background-color: white;
    z-index: 1;
    border-bottom: 1px solid #e8e8e8;
}

/* 搜索框样式
-------------------------------------------------- */
.search-input {
    min-width: 200px;
    max-width: 300px;
    height: 28px;
    padding: 4px 8px;
    border: 1px solid #d9d9d9;
    border-radius: 2px;
    font-size: 12px;
}

.search-input:focus {
    border-color: #f95e34;
    outline: none;
    box-shadow: 0 0 0 2px rgba(249, 94, 52, 0.2);
}

/* 筛选器样式
-------------------------------------------------- */
.filter-container {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.filter-item {
    display: flex;
    align-items: center;
    gap: 4px;
}

.filter-label {
    font-size: 12px;
    color: rgba(0, 0, 0, 0.85);
}

.filter-select {
    height: 28px;
    padding: 4px 8px;
    border: 1px solid #d9d9d9;
    border-radius: 2px;
    font-size: 12px;
    min-width: 100px;
}

.filter-select:focus {
    border-color: #f95e34;
    outline: none;
    box-shadow: 0 0 0 2px rgba(249, 94, 52, 0.2);
}

/* 表格底部样式
-------------------------------------------------- */
.table-footer {
    padding: 8px;
    display: flex;
    align-items: center;
    position: sticky;
    bottom: 0;
    background-color: white;
    z-index: 1;
    border-top: 1px solid #e8e8e8;
}

.data-count {
    font-size: 12px;
    color: rgba(0, 0, 0, 0.65);
    font-family: Microsoft Yahei;
} 

四、实现效果

在这里插入图片描述

五、使用文档 BasicTableModal 表格模态框组件

1.组件简介

BasicTableModal 是一个表格模态框组件,提供了搜索、筛选、自动序号、数据量统计等功能。它适用于需要在模态框中展示表格数据的场景。

2.功能特点

  • 支持表格数据展示
  • 自动添加序号列
  • 支持关键字搜索
  • 支持多列筛选
  • 支持自定义单元格渲染
  • 支持奇偶行样式区分
  • 响应式设计
  • 支持数据量统计显示
  • 支持模态框宽度自定义
  • 支持列宽自定义

3.使用方法

基础用法

import BasicTableModal from './basicTableModal';

const columns = [
  {
    key: 'name',
    title: '姓名'
  },
  {
    key: 'age',
    title: '年龄'
  }
];

const data = [
  { name: '张三', age: 25 },
  { name: '李四', age: 30 }
];

function MyComponent() {
  return (
    <BasicTableModal 
      columns={columns}
      data={data}
      visible={true}
      onClose={() => {}}
    />
  );
}

宽度控制示例

// 设置模态框宽度
<BasicTableModal 
  width="90%"
  columns={columns}
  data={data}
/>

// 设置列宽度
const columns = [
  {
    key: 'index',
    title: '序号',
    width: 80  // 固定像素宽度
  },
  {
    key: 'name',
    title: '姓名',
    width: '150px'  // 带单位的宽度
  },
  {
    key: 'description',
    title: '描述',
    width: '40%'  // 百分比宽度
  }
];

带筛选功能

const columns = [
  {
    key: 'name',
    title: '姓名'
  },
  {
    key: 'status',
    title: '状态',
    filters: [
      { text: '在线', value: 'online' },
      { text: '离线', value: 'offline' }
    ]
  }
];

搜索功能示例

// 多字段组合搜索示例
const columns = [
  {
    key: 'name',
    title: '姓名'
  },
  {
    key: 'code',
    title: '编号'
  },
  {
    key: 'description',
    title: '描述'
  }
];

<BasicTableModal 
  columns={columns}
  data={data}
  searchableKeys={['name', 'code', 'description']}  // 可以同时搜索多个字段
  searchPlaceholder="输入姓名/编号/描述搜索..."
/>

自定义单元格渲染

const columns = [
  {
    key: 'name',
    title: '姓名',
    render: (text, record, index) => <span style={{color: 'red'}}>{text}</span>
  }
];

4.API 说明

Props

参数说明类型必填默认值
columns表格列配置Array-
data表格数据Array[]
visible是否显示模态框booleanfalse
onClose关闭模态框的回调函数function-
title模态框标题string‘表格详情’
searchableKeys可搜索的字段键名数组string[][]
searchPlaceholder搜索框占位文本string‘输入关键字搜索…’
width模态框宽度number/string‘80%’

Column 配置项

参数说明类型必填默认值
key列数据对应的键名string-
title列标题string-
render自定义渲染函数function(text, record, index)-
filters筛选选项配置Array-
width列宽度number/string‘auto’

Filter 配置项

参数说明类型必填
text筛选项显示文本string
value筛选项对应的值any

5.注意事项

  1. 组件会自动在表格最左侧添加序号列
  2. 搜索功能仅在设置 searchableKeys 且不为空数组时显示搜索框
  3. 筛选和搜索可以同时使用
  4. 表格支持奇偶行样式区分,便于数据查看
  5. 模态框宽度可以使用数字(默认像素)或带单位的字符串(如:‘80%’、‘800px’)
  6. 列宽度同样支持数字和带单位的字符串,不设置时自动适应内容宽度
  7. 数据总量显示会实时反映当前筛选/搜索后的数据条数

http://www.kler.cn/a/572481.html

相关文章:

  • leetcode日记(77)子集Ⅱ
  • Apache IoTDB 树表双模型直播回顾(上)
  • pytest中pytest.ini文件的使用
  • python八皇后游戏
  • Java 第十一章 GUI编程(2)
  • 游戏引擎学习第135天
  • AI×电商数据API接口:深度融合,引领未来电商行业浪潮
  • docker默认网段和宿主机环境冲突时的处理
  • pytorch获取模型性能
  • 【GPU使用】如何在物理机和Docker中指定GPU进行推理和训练
  • 一、docker初识
  • 【在线用户监控】在线用户查询、强退用户
  • Java利用JSch进行SFTP进行SSH连接远程服务器进行目录创建与上传文件,ChannelSftp
  • 学单片机能从事什么工作?
  • 【华为OD机考】华为OD笔试真题解析(20)--投篮大赛
  • 安卓广播的使用
  • Elasticsearch:简化大数据分析,使用Elasticsearch进行高效数据聚合
  • LeetCode hot 100—轮转数组
  • visual studio 2022 手工写一个简单的MFC程序
  • [原创](Modern C++)现代C++的关键性概念: 什么是友元函数, 什么是友元类?