在项目开发过程中,对于复杂的业务选择功能很常见,而常用的下拉框选择组件往往不能满足需求,所以这里弄个针对复杂业务的可带过滤条件以及分页的选择组件,且根据不同需求进行扩展或优化。
具体实现:
1、首先新建一个SelectModal的文件夹,内部一个index.js 文件用于组件入口。
/**
* @desc 公共选择对话框组件
*/
import React, { Component } from 'react'
import { Input, Button } from 'antd'
import SearchModal from './components'
const { Search } = Input;
const InputGroup = Input.Group;
class SelectModal extends Component {
state = {
dataModal: {
isVisible: false,
},
// selectData: {
// selectId_value: '',
// selectText_value: '',
// }
selectData: {}
}
handleSearch(value, e) {
this._isOpen(true);
}
handleSearchChange = (e) => {
let value = e.target.value;
this.setState({
selectText_value: value
}, () => {
this.props.onChange(value); // 调用父组件传的回调
})
}
handleSelect = (data, index) => {
const selectId = this.props.config.id;
const selectText = this.props.config.text;
this.setState({
selectData: {
[selectId]: data[selectId],
[selectText]: data[selectText]
}
}, () => {
// 必须针对父组件触发或隐性触发 onchange 事件形成双向绑定,且实际传递的是当前选项值
this.props.onChange(this.state.selectData); // 调用父组件传的回调
})
this._isOpen(false);
}
/**
* 获取当前值 id
*/
getDataId = () => {
const selectId = this.props.config.id;
return this.state.selectData[selectId];
}
/**
* 获取当前值 文本
*/
getDataName = () => {
const selectText = this.props.config.text;
return this.state.selectData[selectText];
}
handleOk = (value) => {
const selectId = this.props.config.id;
const selectText = this.props.config.text;
this.setState({
selectData: {
[selectId]: value[selectId],
[selectText]: value[selectText]
}
}, () => {
// 必须针对父组件触发或隐性触发 onchange 事件形成双向绑定,且实际传递的是当前选项值
this.props.onChange(this.state.selectData); // 调用父组件传的回调
})
this._isOpen(false);
}
afterClose = () => {
this.modalRef.filterRef.current.state.value = ''
}
handleCancel = () => {
this._isOpen(false);
}
_isOpen = (flag) => {
this.setState({
dataModal: {
isVisible: flag
}
})
}
render() {
const { config, value } = this.props;
const { dataModal } = this.state;
return (
<>
<InputGroup compact>
<Input
readOnly
ref={(input)=> this.myinput = input}
value={value[config.text]}
placeholder={config.placeholder}
allowClear
style={{width: '80%'}}/>
<Button type="primary"
icon="search"
style={{width: '20%'}}
onClick={(e) => {this.handleSearch(e)}}/>
</InputGroup>
<SearchModal
modalConfig={config}
ref={modalRef => {this.modalRef = modalRef}}
dataModal={dataModal}
onSelect={this.handleSelect}
onCancel={this.handleCancel}
onOk={this.handleOk}
afterClose={this.afterClose}>
</SearchModal>
</>
)
}
}
export default SelectModal
2、同时在SelectModal文件夹内部新建一个component文件夹,并在该文件家中新建index.js文件,用于存放弹框的逻辑。
/**
* @desc 公共选择对话框组件-对话框
*/
import React, { Component, useRef } from 'react'
import { Form, Input, Select, Modal, Table, Button, message, ConfigProvider, Pagination } from 'antd'
import zhCN from 'antd/es/locale/zh_CN'
import './index.scss'
const InputGroup = Input.Group;
const { Option } = Select;
const { Column } = Table;
class SearchModal extends Component {
constructor(props) {
super(props);
this.state = {
locale: zhCN,
loading: false,
query: {
page: 1,
limit: 5
},
filterLabel: props.modalConfig.searchOptions[0].value,
filterValue: '',
dataSource: [],
selectData: '',
total: 0,
currentRowId: '',
selectedRowKeys: [] // 指定选中项的 key 数组,需要和 onChange 进行配合
}
this.filterRef = React.createRef();
}
componentDidMount() {
this.getDataList();
}
getDataList = () => {
const { getData } = this.props.modalConfig;
this.setState({loading: true});
getData(this.state.query).then((response) => {
this.setState({loading: false});
const dataSource = response.data.data;
const total = response.data.total;
this.setState({dataSource, total});
}).catch(error => {
this.setState({loading: false});
message.error('请求参数错误!')
})
}
handleSearch = () => {
this.setState((state) => ({
query: {
...state.query,
[state.filterLabel]: state.filterValue
},
}), () => {
this.getDataList();
})
}
handleSelectChange = (value) => {
this.filterRef.current.state.value = '';
this.setState((state) => ({
query: {
page: 1,
limit: 5,
[value]: ''
},
filterLabel: value,
filterValue: '',
}))
}
handleInputChange = (e) => {
let value = e.target.value;
this.setState((state) => ({
filterValue: value
}))
}
onRowClick = (record, index) => {
this.setState({
selectData: record,
currentRowId: record.id,
selectedRowKeys: [record.id]
})
}
onRowDoubleClick = (record, index) => {
this.props.onSelect(record, index); // 调用父组件传的回调
}
setRowClassName = (record) => {
return record.id === this.state.currentRowId ? 'currentRowColor' : '';
}
changePage = (page, limit) => {
this.setState(
(state) => ({
query: {
...state.query,
page
}
}), () => {
this.getDataList()
}
)
}
changePageSize = (current, size) => {
this.setState(
(state) => ({
query: {
...state.query,
page: 1,
limit: size
}
}), () => {
this.getDataList()
}
)
}
handleModalOk = () => {
const selectId = this.props.modalConfig.id;
const selectText = this.props.modalConfig.text;
let selectData = {
[selectId]: this.state.selectData[selectId],
[selectText]: this.state.selectData[selectText]
}
this.props.onOk(selectData);
}
render() {
const { modalConfig, dataModal, onCancel, afterClose } = this.props;
const { locale, loading, dataSource, selectedRowKeys, query, total } = this.state;
const rowSelection = { // rowSelection 配置项
type: 'radio',
selectedRowKeys, // 需配合 onChange 使用
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
currentRowId: selectedRows[0].id,
selectedRowKeys
})
}
}
return (
<Modal
title={modalConfig.title}
width={modalConfig.width}
visible={dataModal.isVisible}
maskClosable={false}
onOk={this.handleModalOk}
onCancel={onCancel}
afterClose={afterClose}>
<div style={{marginBottom: '20px'}}>
<Form>
<InputGroup className='selectModalInputGroup' compact style={{width: '100%'}}>
<Select defaultValue={modalConfig.searchOptions[0].value} placeholder="过滤条件" style={{width: '150px'}} onChange={this.handleSelectChange}>
{
modalConfig.searchOptions.map((option, index) => {
return (
<Option key={index} value={option.value}>{option.name}</Option>
)
})
}
</Select>
<Input ref={this.filterRef} allowClear onChange={this.handleInputChange}/>
<Button icon="search" onClick={this.handleSearch}></Button>
</InputGroup>
</Form>
</div>
<Table
rowKey="_id"
size="small"
bordered
loading={ loading }
dataSource={dataSource}
onRow={(record, index) => { // record 指的当前行的数据内容,rowkey指的是当前行的下标
return {
onClick: this.onRowClick.bind(this, record, index), // 点击行
onDoubleClick: this.onRowDoubleClick.bind(this, record, index), // 双击
onContextMenu: event => {}, // 右键菜单
onMouseEnter: event => {}, // 鼠标移入行
onMouseLeave: event => {} // 鼠标移出行
}
}}
rowClassName={this.setRowClassName}
pagination={false}
rowSelection={rowSelection}
scroll={{ y: 400 }}>
{
modalConfig.columns && modalConfig.columns.map((column, index) => {
return (
<Column
fixed={column.fixed}
title={column.title}
dataIndex={column.dataIndex}
key={column.key}
width={column.width} />
)
})
}
</Table>
<br/>
<ConfigProvider locale={ locale }>
{
total > 0 ? (
<>
<Pagination
style={{ textAlign: 'right' }}
size="small"
total={ total }
current={query.page}
defaultPageSize={query.limit}
pageSizeOptions={["5", "10", "15"]}
showSizeChanger
showTotal={total => `总共 ${total} 条`}
onChange={this.changePage}
onShowSizeChange={this.changePageSize}
/>
<br/>
</>
) : null
}
</ConfigProvider>
</Modal>
)
}
}
export default SearchModal
在该文件夹下补上一个css文件index.scss:
.ant-input-group.selectModalInputGroup {
display: flex;
}
.ant-input-group.selectModalInputGroup button {
width: 50px;
}
.currentRowColor {
background-color: #e6f7ff;
}
.ant-table-tbody > tr.ant-table-row-selected.currentRowColor > td,
.ant-table-tbody > tr.ant-table-row-selected.currentRowColor:hover > td {
background-color: #e6f7ff;
}
3、使用
在初始state中,需要传入的一个config配置信息:
state = {
selectOrganConfig: {
title: '选择机构',
width: 800,
placeholder: '请选择机构',
searchOptions: [
{ name: '机构名称', value: 'name' }
],
columns: [
{
title: '机构ID',
width: 300,
dataIndex: '_id',
key: '_id'
},
{
title: '机构名称',
dataIndex: 'name',
key: 'name'
}
],
id: 'id', // 值 id 字段
text: 'name', // 值 text 字段
getData: getOrgans
}
}
如果结合antd框架的Form表单使用,有
<Form.Item label="所属机构" >
{getFieldDecorator('organ', {
rules: [{ required: true, message: '请选择机构!' }],
initialValue: organData
// 注:initialValue 此时 对应到 组件 默认的 value 上
})(
<SelectModal
ref={ref => {this.organRef = ref}}
onChange={this.handleOrganChange}
config={selectOrganConfig}/>
)}
</Form.Item>
对于组件本身而言,可以优化扩展的地方很多,如何在使用组件时更为简便,便于开发使用,需要考虑的因素还有很多。