封裝通用組件 一、封裝思想 二、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' ;
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 } ; } 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 ( < selectclassName= "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 ) && ( < inputtype= "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 是否顯示模態框 boolean 否 false 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.注意事項
組件會自動在表格最左側添加序號列 搜索功能僅在設置 searchableKeys 且不為空數組時顯示搜索框 篩選和搜索可以同時使用 表格支持奇偶行樣式區分,便于數據查看 模態框寬度可以使用數字(默認像素)或帶單位的字符串(如:‘80%’、‘800px’) 列寬度同樣支持數字和帶單位的字符串,不設置時自動適應內容寬度 數據總量顯示會實時反映當前篩選/搜索后的數據條數