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组件
上一篇:Ant Design Pro开发后台管理系统(Table)


下一篇:Ant Design Pro开发后台管理系统(新增页面)