Search 搜尋,提供一般清單頁搜尋,本文簡單講述一下設計思想。
先看一下效果圖
元件隻封裝了 RangePicker、Input、Select三個搜尋條件,可以繼續擴充

圖1.我們要做什麼
為什麼要封裝Search元件?
如圖1清單頁帶搜尋,運用場景很多,而且搜尋條件有多有少,搜尋類型也多有不同,産品還時不時的加或者删幾個搜尋條件......如果不封裝的話我們每次使用都要引入大量antd元件代碼,寫一大堆Form,完全不不了一行元件,三兩個參數簡潔,美觀。
封裝後的使用 (文章最後附上元件源碼)
const items = [{
type: 'Input',
label: 'ID查詢',
required: false,
placeholder: '請輸入id',
parameter: 'id',
}, {
type: 'Select',
label: '科目查詢',
required: false,
placeholder: '請選擇',
parameter: 'subject',
options: []
}];
<Search items={items} loading={loading} onSubmit={()=>{}} onReset={()=>{}}/>
如此便生成圖1的搜尋元件,一次封裝,N次受益,我們再也不需要寫那些antd 的Form了,我們隻需要看産品要求的搜尋條件都是什麼,去配置items(array)就可以了,具體參數下文會詳細解說,然後再把搜尋方法和重置方法的業務邏輯加上,整個搜尋就完成了。
Search元件的設計思路 “用對象生成UI”
我們把Search整體看做一個大的容器,其中包含了“一定一不定”,操作方法一定(送出、重置),輸入條件不一定,我所說的一定不一定指的是UI顯示上。
“一定”:所有的搜尋基本上都需要“送出”、“重置”兩個按鈕。
“送出”就是把輸入的搜尋資訊送出到背景擷取到清單資料,但是每個頁面的搜尋接口不一樣,資料處理或許也有差別,是以我們把送出方法抛出來,用的時候把方法傳入就可以。
“重置”就是還原資料,重置搜尋條件,清空搜尋框,顯示預設清單資料
“一不定”:輸入條件不一定,條件個數不一定,不過輸入條件一般就是輸入名稱搜尋、或者類型搜尋(下拉框)、或者時間搜尋
需要提取的公共部分
1、生成ui的數組
2、送出、重置方法
我們着重講一下對象生成UI的概念
首先我們把每一個搜尋條件看成一個“框”
“框”之間的差别:
類型不一樣Input、Select...,
送出參數名字不一樣,
搜尋框名字不一樣
...
(我們預設一行顯示三個元件,缺憾,待完善)
/**
* items [{ | array | 數組包含元素對象
* type | string | 類型 判斷是選擇還是輸入 名字按照antd元件名字傳入
* label | string | 标題
* required | boolean | 是否必填項 true / false
* placeholder | string | 描述
* parameter | string | 參數名字
* options | array | 如果type為Select必須傳,否則認為此元件是Input
* pattern | | 正則
* }]
* onSubmit | function | 送出方法
* onReset | function | 重置方法
* */
以上就是現階段元件的全部參數
由于我們沒有把每行顯示的個數抛出去,而且在元件内部定義好的3,所有當我們拿到items數組的時候就能知道,我們需要幾行才能夠顯示
其中this.colLength=3;
//根據items長度判斷需要顯示幾行
getChildren(items, getFieldDecorator) {
const len = items.length;
const rowLen = Math.ceil(len / this.colLength);
let rowArr = [];
for (let i = 0, j = rowLen; i < j; i++) {
rowArr.push(
<Row key={i}>
{
this.getColItem(items, getFieldDecorator, i)
}
</Row>
);
}
return rowArr;
}
判斷每行裡邊有幾個元件比較步驟多一些,不多說直接貼代碼就不多說了,直接看源碼,其中就是把數組裡邊的對象正确的顯示
import React, {Component} from 'react';
import {
Form,
Row,
Col,
Input,
Select,
Button,
DatePicker,
} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const {RangePicker} = DatePicker;
/**
* lixin 2013.4.19
* items [{ | array | 數組包含元素對象
* type | string | 類型 判斷是選擇還是輸入 名字按照antd元件名字傳入
* label | string | 标題
* required | boolean | 是否必填項 true / false
* placeholder | string | 描述
* parameter | string | 參數名字
* options | array | 如果type為Select必須傳,否則認為此元件是Input
* pattern | | 正則
* typeDiff | boolean | true type為Select的時候,key為string的時候會用到
* }]
* onSubmit | function | 送出方法
* onReset | function | 重置方法
* 未完待續...
*
* */
@Form.create()
class Search extends Component {
constructor(props) {
super(props);
this.colLength = 3;//每行顯示個數,暫時因為栅格布局不能修改
}
//根據items長度判斷需要顯示幾行
getChildren(items, getFieldDecorator) {
const len = items.length;
const rowLen = Math.ceil(len / this.colLength);
let rowArr = [];
for (let i = 0, j = rowLen; i < j; i++) {
rowArr.push(
<Row key={i}>
{
this.getColItem(items, getFieldDecorator, i)
}
</Row>
);
}
return rowArr;
}
//為每行裡邊塞Col
getColItem(items, getFieldDecorator, start) {
const colArr = [];
const _this = this;
//從items數組第幾個元素開始循環
const _start = start * this.colLength;
//剩餘幾個對象沒有周遊渲染
const _surplus = items.length - _start;
let len;
//如果剩下的小于3 長度直接登入items的長度
if (_surplus < this.colLength) {
len = items.length;
} else {
//如果剩下的大于3,那麼長度等于開始索引加3
len = _start + this.colLength;
}
for (let i = _start, j = len; i < j; i++) {
const index = i;
const value = items[i];
const _offset = index % this.colLength == 0 ? 0 : 1;
let _type = value.type;
let _options;
if (value.hasOwnProperty('options')) {
_options = value.options;
} else {
if (_type === 'RangePicker') {
//
} else {
_type = 'Input';
}
}
let _rulesType = 'number';
if (_type === 'Input') {
_rulesType = 'string';
} else if (_type === 'RangePicker') {
_rulesType = 'array';
}
if (value.typeDiff) {
_rulesType = 'string';
}
const formItemLayout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
colArr.push(
<Col key={index} xl={{span: 7, offset: _offset}} lg={{span: 8}} md={{span: 12}} sm={24}>
<FormItem label={`${value.label}:`} {...formItemLayout}>
{getFieldDecorator(value.parameter, {
rules: [{
required: value.required,
message: value.placeholder,
pattern: value.pattern ? value.pattern : '',
type: _rulesType
}]
})(
_this.switchItem(_type, value.placeholder, _options)
)}
</FormItem>
</Col>
)
}
return colArr;
}
//如果是Select需要傳入options
switchItem(which, placeholder, options) {
const _this = this;
switch (which) {
case 'Input':
return <Input placeholder={placeholder}/>
case 'Select':
return <Select placeholder={placeholder}>{_this.getOption(options)}</Select>
case 'RangePicker':
return <RangePicker/>
}
}
getOption(data) {
if (!data) {
return;
}
return data.map((value, index) => {
return <Option key={index} value={value.key}>{value.value}</Option>
})
}
//重置輸入框内容
handleReset = () => {
this.props.form.resetFields();
if (this.props.onReset) {
this.props.onReset();
}
}
//送出
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, fieldsValue) => {
this.props.onSubmit(err, fieldsValue);
});
};
render() {
const {items, form, loading} = this.props;
const {getFieldDecorator} = form;
return (
<Form onSubmit={this.handleSubmit} layout="horizontal">
{
this.getChildren(items, getFieldDecorator)
}
<Row type="flex" justify="end">
<Col>
<FormItem>
<Button type="default" htmlType="button" style={{marginRight: '10px'}} onClick={this.handleReset}>
重置
</Button>
<Button loading={loading} type="primary" htmlType="submit">
查詢
</Button>
</FormItem>
</Col>
</Row>
{
this.props.children
}
</Form>
)
}
}
export default Search;
其中比較特殊的就是當type為Select的時候,這個時候必須傳參數options(array)數組,因為下來菜單我們要有選項集,如果不傳我們預設它是一個Input元件