内容简介:最近把新的后台系统写好了..用的是上篇文章的技术栈(但是感觉仔细梳理了下上个系统,发现可以抽离的东西不少
最近把新的后台系统写好了..用的是上篇文章的技术栈( mobx+react16
);
但是感觉 mobx
没有想象中的好用,看到 umi 2.x
了.就着手又开始重构了...
仔细梳理了下上个系统,发现可以抽离的东西不少
有兴趣的瞧瞧,没兴趣的止步,节约您的时间...
效果图
- 响应式传入
-
折叠展开搜索条件,默认六个隐藏展开按钮,大于则显示(点击直接取数据源的长度)
-
传递子组件作为搜索按钮区域
抽离思路及实现
思路
- 合并
props
传递的值,尽可能的减少传递的东西(在组件内部实现默认值合并),把渲染的子组件通过遍历json
去实现; - 整个查询区域用的
antd
表单组件,聚合所有表单数据(自动双向绑定,设置默认值等); - 为了降低复杂度,子组件不考虑
dva
来维护状态,纯靠props
和state
构建,然后统一把构建的表单数据向父级暴露.. - 内部的state默认初始化都为空[
antd
对于日期控件使用null
来置空],外部初始化可以用getFieldDecorator
的initialValue
,已经暴露
实现的功能
支持的props
根据 ctype
渲染的控件有 Input,Button,Select,DatePicker,Cascader,Radio
允许传递的props有三个,所有props均有默认值,传递的会合并进去
data accumulate responseLayout getSearchFormData
<AdvancedSearchForm data={searchItem} getSearchFormData={this.searchList} accumulate="3"> <Button type="dashed" icon="download" style={{ marginLeft: 8 }} htmlType="submit"> 下载报表 </Button> </AdvancedSearchForm> 复制代码
数据源格式
data
的数据格式基本和 antd
要求的格式一致,除了个别用来判断或者渲染子组件的,
标准格式为:
ctype(controller-type:控件类型) attr(控件支持的属性) field(受控表单控件的配置项)
searchItem: [ { ctype: 'dayPicker', attr: { placeholder: '查询某天', }, field: { label: '日活', value: 'activeData', }, }, { ctype: 'monthPicker', attr: { placeholder: '查询月份数据', }, field: { label: '月活', value: 'activeData', }, }, { ctype: 'radio', field: { label: '设备类型', value: 'platformId', params: { initialValue: '', }, }, selectOptionsChildren: [ { label: '全部', value: '', }, { label: '未知设备', value: '0', }, { label: 'Android', value: '1', }, { label: 'IOS', value: '2', }, ], }, { ctype: 'cascader', field: { label: '排序', value: 'sorter', }, selectOptionsChildren: [ { label: '根据登录时间', value: 'loginAt', children: [ { label: '升序', value: 'asc', }, { label: '降序', value: 'desc', }, ], }, { label: '根据注册时间', value: 'createdAt', children: [ { label: '升序', value: 'asc', }, { label: '降序', value: 'desc', }, ], }, ], }, ], 复制代码
实现代码
AdvancedSearchForm
index.js
import { PureComponent } from 'react'; import { Form, Row, Col, Input, Button, Select, DatePicker, Card, Cascader, Radio, Icon, } from 'antd'; const { MonthPicker, RangePicker } = DatePicker; const Option = Select.Option; const FormItem = Form.Item; const RadioButton = Radio.Button; const RadioGroup = Radio.Group; @Form.create() class AdvancedSearchForm extends PureComponent { state = { expand: false, factoryData: [ { ctype: 'input', attr: { placeholder: '请输入查询内容...', }, field: { label: '', value: '', params: { initialValue: '', }, }, }, { ctype: 'select', attr: { placeholder: '请选择查询项', allowClear: true, }, selectOptionsChildren: [], field: { label: '', value: '', params: { initialValue: '', }, }, }, { ctype: 'cascader', attr: { placeholder: '请选择查询项', allowClear: true, }, selectOptionsChildren: [], field: { label: '', value: [], params: { initialValue: [], }, }, }, { ctype: 'dayPicker', attr: { placeholder: '请选择日期', allowClear: true, format: 'YYYY-MM-DD', }, field: { label: '', value: '', params: { initialValue: null, }, }, }, { ctype: 'monthPicker', attr: { placeholder: '请选择月份', allowClear: true, format: 'YYYY-MM', }, field: { label: '', value: '', params: { initialValue: null, }, }, }, { ctype: 'timerangePicker', attr: { placeholder: '请选择日期返回', allowClear: true, }, field: { label: '', value: '', params: { initialValue: [null, null], }, }, }, { ctype: 'radio', attr: {}, field: { label: '', value: '', params: { initialValue: '', }, }, }, ], }; // 获取props并且合并 static getDerivedStateFromProps(nextProps, prevState) { /** * data: 构建的数据 * single: 单一选择,会禁用其他输入框 * mode: coallpse(折叠) */ const { factoryData } = prevState; const { data, csize } = nextProps; let newData = []; if (data && Array.isArray(data) && data.length > 0) { // 合并传入的props data.map(item => { // 若是有外部传入全局控制表单控件大小的则应用 if (csize && typeof csize === 'string') { item.attr = { ...item.attr, size: csize, }; } const { ctype, attr, field, ...rest } = item; let combindData = {}; factoryData.map(innerItem => { if (item.ctype === innerItem.ctype) { const { ctype: innerCtype, attr: innerAttr, field: innerField, ...innerRest } = innerItem; combindData = { ctype: item.ctype, attr: { ...innerAttr, ...attr, }, field: { ...innerField, ...field, }, ...innerRest, ...rest, }; } }); newData.push(combindData); }); // 返回合并后的数据,比如mode,渲染的数据这些 return { factoryData: newData }; } return null; } // 提交表单 handleSearch = e => { e.preventDefault(); this.props.form.validateFields((err, values) => { if (!err) { this.props.getSearchFormData(values); } }); }; // 重置表单 handleReset = () => { this.props.form.resetFields(); }; // 生成 Form.Item getFields = () => { const { factoryData } = this.state; const children = []; if (factoryData) { for (let i = 0; i < factoryData.length; i++) { // 若是控件的名字丢.亦或filed的字段名或之值丢失则不渲染该组件 // 若是为select或cascader没有子组件数据也跳过 const { ctype, field: { value, label }, selectOptionsChildren, } = factoryData[i]; if ( !ctype || !value || !label || ((ctype === 'select' || ctype === 'cascader') && selectOptionsChildren && selectOptionsChildren.length < 1) ) continue; // 渲染组件 let formItem = this.renderItem({ ...factoryData[i], itemIndex: i, }); // 缓存组件数据 children.push(formItem); } return children; } else { return []; } }; // 合并响应式props combindResponseLayout = () => { const { responseLayout } = this.props; // 响应式 return { xs: 24, sm: 24, md: 12, lg: 8, xxl: 6, ...responseLayout, }; }; // 计算外部传入需要显示隐藏的个数 countHidden = () => { const { data, accumulate } = this.props; return this.state.expand ? data.length : accumulate ? accumulate : 6; }; // 判断需要渲染的组件 renderItem = data => { const { getFieldDecorator } = this.props.form; const { ctype, field, attr, itemIndex } = data; const ResponseLayout = this.combindResponseLayout(); const count = this.countHidden(); switch (ctype) { case 'input': return ( <Col {...ResponseLayout} style={{ display: itemIndex < count ? 'block' : 'none' }} key={Math.random() * 1000000} > <FormItem label={field.label}> {getFieldDecorator(field.value, field.params ? field.params : {})( <Input {...attr} /> )} </FormItem> </Col> ); case 'select': return ( <Col {...ResponseLayout} style={{ display: itemIndex < count ? 'block' : 'none' }} key={Math.random() * 1000000} > <FormItem label={field.label}> {getFieldDecorator(field.value, field.params ? field.params : {})( <Select {...attr}> {data.selectOptionsChildren && data.selectOptionsChildren.length > 0 && data.selectOptionsChildren.map((optionItem, index) => ( <Option value={optionItem.value} key={index}> {optionItem.label} </Option> ))} </Select> )} </FormItem> </Col> ); case 'cascader': return ( <Col {...ResponseLayout} style={{ display: itemIndex < count ? 'block' : 'none' }} key={Math.random() * 1000000} > <FormItem label={field.label}> {getFieldDecorator(field.value, field.params ? field.params : {})( <Cascader {...attr} options={data.selectOptionsChildren} /> )} </FormItem> </Col> ); case 'dayPicker': return ( <Col {...ResponseLayout} style={{ display: itemIndex < count ? 'block' : 'none' }} key={Math.random() * 1000000} > <FormItem label={field.label}> {getFieldDecorator(field.value, field.params ? field.params : {})( <DatePicker {...attr} /> )} </FormItem> </Col> ); case 'monthPicker': return ( <Col {...ResponseLayout} style={{ display: itemIndex < count ? 'block' : 'none' }} key={Math.random() * 1000000} > <FormItem label={field.label}> {getFieldDecorator(field.value, field.params ? field.params : {})( <MonthPicker {...attr} /> )} </FormItem> </Col> ); case 'timerangePicker': return ( <Col {...ResponseLayout} style={{ display: itemIndex < count ? 'block' : 'none' }} key={Math.random() * 1000000} > <FormItem label={field.label}> {getFieldDecorator(field.value, field.params ? field.params : {})( <RangePicker {...attr} /> )} </FormItem> </Col> ); case 'datePicker': return ( <Col {...ResponseLayout} style={{ display: itemIndex < count ? 'block' : 'none' }} key={Math.random() * 1000000} > <FormItem label={field.label}> {getFieldDecorator(field.value, field.params ? field.params : {})( <DatePicker {...attr} /> )} </FormItem> </Col> ); case 'radio': return ( <Col {...ResponseLayout} style={{ display: itemIndex < count ? 'block' : 'none' }} key={Math.random() * 1000000} > <FormItem label={field.label}> {getFieldDecorator(field.value, field.params ? field.params : {})( <RadioGroup {...attr}> {data.selectOptionsChildren && data.selectOptionsChildren.length > 0 && data.selectOptionsChildren.map((optionItem, index) => ( <RadioButton value={optionItem.value} key={index}> {optionItem.label} </RadioButton> ))} </RadioGroup> )} </FormItem> </Col> ); default: return null; } }; // 折叠搜索框条件 toggle = () => { const { expand } = this.state; this.setState({ expand: !expand }); }; render() { const { expand } = this.state; const { data, children } = this.props; return ( <Form className="ant-advanced-search-form" onSubmit={this.handleSearch}> <Card title="搜索区域" extra={ <> <Button type="primary" htmlType="submit"> 搜索 </Button> <Button style={{ marginLeft: 8 }} onClick={this.handleReset}> 清除 </Button> {children ? <>{children}</> : null} </> } style={{ width: '100%' }} > <Row gutter={24} type="flex" justify="start"> {this.getFields()} </Row> {data && data.length === 3 ? null : ( <Row gutter={24} type="flex" justify="center"> <a onClick={this.toggle}> <Icon type={expand ? 'up' : 'down'} />{' '} </a> </Row> )} </Card> </Form> ); } } export default AdvancedSearchForm; 复制代码
index.css
// 列表搜索区域 .ant-advanced-search-form { border-radius: 6px; } .ant-advanced-search-form .ant-form-item { display: flex; flex-wrap: wrap; } .ant-advanced-search-form .ant-form-item-control-wrapper { flex: 1; } 复制代码
总结
温馨提示
- 没用
prop-types
, 感觉没必要...(若是用ts
的小伙伴,运行时类型推断比这个强大的多,还不会打包冗余代码) - 没发布
npm
, 只是提供我写的思路,对您有没有帮助,见仁见智 - 依赖
moment
,antd
可以自行拓展的点
- 比如垂直展示
- 比如表单校验(关联搜索条件[就是必须有前置条件才能搜索])
学无止境,任重而道远...
有不对之处尽请留言,会及时修正,谢谢阅读
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- React 折腾记 - (8) 基于React+Antd封装选择单个文章分类(从构建到获取)
- Vue 折腾记 - (19) 基于Antd Design Vue 封装一个符合业务的树形组件
- React 折腾记 - (7) 基于React+Antd封装聊天记录(用到memo,lazy, Suspense)这些
- React 折腾记 - (9) 基于Antd+react-router-breadcrumbs-hoc封装一个小巧的面包屑组件
- postcss"折腾"记
- 十年博客折腾历史
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Computer Age Statistical Inference
Bradley Efron、Trevor Hastie / Cambridge University Press / 2016-7-21 / USD 74.99
The twenty-first century has seen a breathtaking expansion of statistical methodology, both in scope and in influence. 'Big data', 'data science', and 'machine learning' have become familiar terms in ......一起来看看 《Computer Age Statistical Inference》 这本书的介绍吧!