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

news2025/3/5 23:42:01

封装通用组件

  • 一、封装思想
  • 二、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.coloradmin.cn/o/2310247.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaEE】-- 多线程(初阶)4

文章目录 8.多线程案例8.1 单例模式8.1.1 饿汉模式8.1.2 懒汉模式 8.2 阻塞队列8.2.1 什么是阻塞队列8.2.2 生产者消费者模型8.2.3 标准库中的阻塞队列8.2.4 阻塞队列的应用场景8.2.4.1 消息队列 8.2.5 异步操作8.2.5 自定义实现阻塞队列8.2.6 阻塞队列--生产者消费者模型 8.3 …

WP 高级摘要插件:助力 WordPress 文章摘要精准自定义显示

wordpress插件介绍 “WP高级摘要插件”功能丰富&#xff0c;它允许用户在WordPress后台自定义文章摘要。 可设置摘要长度&#xff0c;灵活调整展示字数&#xff1b;设定摘要最后的显示字符&#xff0c; 如常用的省略号等以提示内容未完整展示&#xff1b;指定允许在摘要中显示…

论文阅读 EEG-Inception

EEG-Inception: A Novel Deep Convolutional Neural Network for Assistive ERP-Based Brain-Computer Interfaces EEG-Inception是第一个集成Inception模块进行ERP检测的模型&#xff0c;它有效地结合了轻型架构中的其他结构&#xff0c;提高了我们方法的性能。 本研究的主要目…

FFmpeg入门:最简单的音频播放器

FFmpeg入门&#xff1a;最简单的音频播放器 欢迎大家来到FFmpeg入门的第二章&#xff0c;今天只做一个最简单的FFmpeg音频播放器&#xff1b;同样&#xff0c;话不多说&#xff0c;先上流程图 流程图 以上流程和视频播放器的解码过程基本上是一致的&#xff1b; 不同点在于 S…

基于Linux系统的物联网智能终端

背景 产品研发和项目研发有什么区别&#xff1f;一个令人发指的问题&#xff0c;刚开始工作时项目开发居多&#xff0c;认为项目开发和产品开发区别不大&#xff0c;待后来随着自身能力的提升&#xff0c;逐步感到要开发一个好产品还是比较难的&#xff0c;我认为项目开发的目的…

Netty笔记9:粘包半包

Netty笔记1&#xff1a;线程模型 Netty笔记2&#xff1a;零拷贝 Netty笔记3&#xff1a;NIO编程 Netty笔记4&#xff1a;Epoll Netty笔记5&#xff1a;Netty开发实例 Netty笔记6&#xff1a;Netty组件 Netty笔记7&#xff1a;ChannelPromise通知处理 Netty笔记8&#xf…

LabVIEW虚拟弗兰克赫兹实验仪

随着信息技术的飞速发展&#xff0c;虚拟仿真技术已经成为教学和研究中不可或缺的工具。开发了一种基于LabVIEW平台开发的虚拟弗兰克赫兹实验仪&#xff0c;该系统不仅能模拟实验操作&#xff0c;还能实时绘制数据图形&#xff0c;极大地丰富了物理实验的教学内容和方式。 ​ …

spring boot + vue 搭建环境

参考文档&#xff1a;https://blog.csdn.net/weixin_44215249/article/details/117376417?fromshareblogdetail&sharetypeblogdetail&sharerId117376417&sharereferPC&sharesourceqxpapt&sharefromfrom_link. spring boot vue 搭建环境 一、浏览器二、jd…

清华团队提出HistoCell,从组织学图像推断超分辨率细胞空间分布助力癌症研究|顶刊精析·25-03-02

小罗碎碎念 今天和大家分享一篇2025-02-21发表于nature communications的文章&#xff0c;内容涉及病理空转单细胞。 从组织学图像推断细胞空间分布对癌症研究意义重大&#xff0c;但现有方法存在标注工作量大、分辨率或特征挖掘不足等局限。研究旨在开发一种高效准确的方法。 …

html+js 轮播图

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>轮播图示例</title><style>/* 基本样式…

vue3:初学 vue-router 路由配置

承上一篇&#xff1a;nodejs&#xff1a;express js-mdict 作为后端&#xff0c;vue 3 vite 作为前端&#xff0c;在线查询英汉词典 安装 cnpm install vue-router -S 现在讲一讲 vue3&#xff1a;vue-router 路由配置 cd \js\mydict-web\src mkdir router cd router 我还…

Python 爬取唐诗宋词三百首

你可以使用 requests 和 BeautifulSoup 来爬取《唐诗三百首》和《宋词三百首》的数据。以下是一个基本的 Python 爬虫示例&#xff0c;它从 中华诗词网 或类似的网站获取数据并保存为 JSON 文件。 import requests from bs4 import BeautifulSoup import json import time# 爬取…

C语言408考研先行课第一课:数据类型

由于408要考数据结构……会有算法题…… 所以&#xff0c;需要C语言来进行一个预备…… 因为大一贪玩&#xff0c;C语言根本没学进去……谁能想到考研还用得到呢&#xff1f;【手动doge&#xff08;bushi&#xff09; 软件用的是Clion&#xff0c;可以自行搜索教程下载使用。…

03 HarmonyOS Next仪表盘案例详解(二):进阶篇

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; 文章目录 前言1. 响应式设计1.1 屏幕适配1.2 弹性布局 2. 数据展示与交互2.1 数据卡片渲染2.2 图表区域 3. 事件处理机制3.1 点击事件处理3.2 手势…

探秘基带算法:从原理到5G时代的通信变革【四】Polar 编解码(一)

文章目录 2.3 Polar 编解码2.3.1 Polar 码简介与发展背景2.3.2 信道极化理论基础对称容量与巴氏参数对称容量 I ( W ) I(W) I(W)巴氏参数 Z ( W ) Z(W) Z(W)常见信道信道联合信道分裂信道极化 本博客为系列博客&#xff0c;主要讲解各基带算法的原理与应用&#xff0c;包括&…

基础篇(一)强化学习是什么?从零开始理解智能体的学习过程

强化学习是什么&#xff1f;从零开始理解智能体的学习过程 你是否曾好奇过&#xff0c;人工智能是如何在复杂的环境中学会做出决策的&#xff1f;无论是打游戏的AI&#xff0c;还是自动驾驶的汽车&#xff0c;还是最近很火的DeepSeek它们的背后都离不开一种强大的技术——强化…

如何直接导出某个conda环境中的包, 然后直接用 pip install -r requirements.txt 在新环境中安装

1. 导出 Conda 环境配置 conda list --export > conda_requirements.txt这将生成一个 conda_requirements.txt 文件&#xff0c;其中包含当前环境中所有包的列表及其版本信息。 2. 转换为 requirements.txt 文件 grep -v "^#" conda_requirements.txt | cut -d …

基于 HTML、CSS 和 JavaScript 的智能九宫格图片分割系统

目录 1 前言 2 技术实现 2.1 HTML 结构 2.2 CSS 样式 2.3 JavaScript 交互 3 代码解析 3.1 HTML 部分 3.2 CSS 部分 3.3 JavaScript 部分 4 完整代码 5 运行结果 6 总结 6.1 系统特点 6.2 使用方法 1 前言 在当今数字化的时代&#xff0c;图片处理需求日益增长。…

委托者模式(掌握设计模式的核心之一)

目录 问题&#xff1a; 举例&#xff1a; 总结&#xff1a;核心就是利用Java中的多态来完成注入。 问题&#xff1a; 今天刷面经&#xff0c;刷到装饰者模式&#xff0c;又进阶的发现委托者模式&#xff0c;发现还是不理解&#xff0c;特此记录。 举例&#xff1a; ​老板​…

MySQL-高级查询

查询处理 排序&#xff08;默认不是按主键排序的&#xff09; order by 字段1[&#xff0c;字段2] [asc|desc] 默认是升序排序也可以指定 select 列表中列的序号进行排序如果是多个字段&#xff0c;那么在上一个字段排序完的基础上排序下一个 限制数量 limit 行数&#xff0…