天天看點

Ant Design Pro開發背景管理系統(Search)

Search 搜尋,提供一般清單頁搜尋,本文簡單講述一下設計思想。

先看一下效果圖

元件隻封裝了 RangePicker、Input、Select三個搜尋條件,可以繼續擴充

Ant Design Pro開發背景管理系統(Search)

圖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元件

繼續閱讀