antd checkbox 默认选中_antd 开发的一些坑(一)

使用antd以来,有些东西总是现用现查,现总结一波,以提升开发效率

一:表格下钻及默认展开一级

{
              treeData && treeData.length > 0 ? (
                <Table
                  rowKey={record => `${record.deptCode}`}
                  loading={tableLoading}
                  defaultExpandedRowKeys={treeData.map(item => item.deptCode)} 
                  columns={this.state.columns}
                  dataSource={treeData}
                  pagination={false}
                  onExpand={this.handldOnExpand}
                  scroll={{ y: 450 }}
                  
                />
              ) : (<div style={{ minHeight: 450, textAlign: 'center', boxSizing: 'border-box', paddingTop: '200px' }}><Spin /></div>)
  }

解释一: 
      modal里面的table,为什么要判断 treeData.length>0? 再展示table? 因为这个treedata的值是在state: treeData:[],上定义的,接口返回再赋值,如果不判断的话,组件初次render之后,
就会执行defaultExpandedRowKeys={treeData.map(item => item.deptCode)} ,等数据回来,第一级数据就不会展开了,因为初次识别的是一个空数组,所以,这里判断
length>0才展示就会默认展示返回的一级数据了。

解释二:
   根据部门的deptCodePaths值判断的
   onExpand={this.handldOnExpand}是按照部门数下钻的,所以,这里有三个函数比较重要,注意理解:
    // 展开加号
  handldOnExpand = async (expanded, record) => {
    if (!expanded || record.children.length) return
    let {
      treeData,
    } = this.state
    let res = await this.getTableChildrenData({ deptIsUserChoose: false, deptCodePaths: [record.deptCodePath] })
    let node = this.findTreeNode(record.deptCode, treeData)
    node.children = res
    this.setState({ tableLoading: false })
  }

  //根据传入的nodeId查找到节点
  findTreeNode = (nodeId, treeList) => {
    let node = null
    if (!treeList || treeList === []) {
      return []
    }
    for (const value of treeList) {
      if (+value.deptCode === +nodeId) {
        node = value
        return node
      } else if (Array.isArray(value.children)) {
        node = this.findTreeNode(nodeId, value.children)
        if (node) return node
      }
    }
  }

  // 得到子节点
  getTableChildrenData({ deptIsUserChoose = false, deptCodePaths = [] }) {
   
    let {
      tableLoading,
    } = this.state
    if (tableLoading) return
    this.setState({ tableLoading: true })
    return  // this.props.enpsStore.getEnpsSituationDetail({ 掉接口
      // ...this.props.searchParams,
      // deptIsUserChoose,
     //  deptCodePaths
   // }).then(res => res)
   
  }

二: select多选一行显示可滑动:

1e6259436619da612601df8a70dd9f76.png

注意:如果只是这一个需要这样的话,那么就需要在外面套一个盒子,进行局部更改,否则,更改的就是全局的了

// treeSelect 
.ant-select-selection--multiple{
  .ant-select-selection__rendered {
    display: flex;
    overflow: auto;
    >li {
      flex-shrink: 0;
    }
    &::-webkit-scrollbar {
      display: none;
    }
  }
}

// 多选select
  .ant-select-selection--multiple {
            width: 100%;
            .ant-select-selection__rendered {
              height: 30px;
              ul {
                display: flex;
                width: 100%;
                overflow: auto;
                &::-webkit-scrollbar {
                  display: none;
                }
                >li {
                  flex-shrink: 0;
                }
              }
            }
          }

如果遇到选中抖动的情况,有可能是line-height影响的,可以加上:(外层封一个盒子)
.chart_statement { // 外层盒子,免得干扰了全局
  .ant-form-item-label {
    line-height: 31px !important;
  }
  .ant-form-item-control {
    line-height: 25px !important
  }
}

三:upload手动上传

即:不是及时上传,是先保存文件,然后,点击提交按钮之后再上传,注意文件的类型和写法:

需要转化成formdata格式上传:

this.state = {
      buttonLoading: false,
      fileList: [],
      disableBtn: false,
    };

 handleSubmit = (e) => {
    e.preventDefault();
    this.props.form.validateFields((err, values) => {
      if (!err) {
        const { fileList } = this.state;
        // console.log('filelist----', fileList[0]);
        const formData = new FormData();
        fileList.forEach((file) => {
          formData.append('file', file);
        });
        // console.log('file----', formData.get('file'))
        this.props.appContext.adminActions.uploadBillFile({
          file: formData,
          periodId: values.name,
          templateId: values.belong,
        }).then((res) => {
          if (res.msg === '成功') {
            message.success('上传成功');
          } else {
            message.error(`${res.msg}`);
          }
        })
        this.setState({
          fileList: [],
          disableBtn: false,
        })
        this.onCancel('reload')
      }
    });
  }

render: (){
   const props = {
      onRemove: (file) => {
        this.setState((state) => {
          const index = state.fileList.indexOf(file);
          const newFileList = state.fileList.slice();
          newFileList.splice(index, 1);
          return {
            fileList: newFileList,
          };
        }, () => {
          if (this.state.fileList < 1) {
            this.setState({
              disableBtn: false,
            })
          }
        });
      },
      beforeUpload: (file) => {
        this.setState(state => ({
          fileList: [...state.fileList, file],
        }), () => {
          if (this.state.fileList.length >= 1) {
            this.setState({
              disableBtn: true, // 如果上传的有一个文件了,就不让上传,用户删除当前文件之后放可上传
            })
          }
        });

        return false;
      },
      fileList,
    };
 
}

// 组件显示
<Upload
      {...props}
      accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
       >

       <Button style={{ width: '400px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '14px' }} disabled={this.state.disableBtn}>
          <Icon type="cloud-upload" style={{ fontSize: '16px' }} />
              请上传文件
       </Button>
</Upload>

// upload上没有action,是先转化成流文件,再传给后端的

// 这里有一个坑,就是我封装的request自定义了请求头,导致文件上传不对,所以,修改之后的request是:
async function uploadRequest(url, options = {}) {
  const response = await fetch(url, Object.assign({
    headers: {
      'x-kylin-proxy-with': 'apirequest', // 用于麒麟 未登录接口不会302而是返回接口真实数据
    },
    credentials: 'include',
    responseType: 'arraybuffer',
  }, options));

  const data = await response.json();
  if (data) {
    return data;
  }
}

export default uploadRequest;

headers看下是否需要带,不要自定义'Content-Type': 'application/json;charset=UTF-8',注意了

四:对于顶部搜索的栅格布局,常用的是这种类型

 const formItemLayout = {
      labelCol: {
        xs: { span: 4 },
        md: { span: 6 },
      },
      wrapperCol: {
        xs: { span: 20 },
        md: { span: 18 },
      },
    };

 <Row type="flex" justify="start" align="top">
              <Col span={8}>
                <FormItem {...formItemLayout} label="费用类型">
                  {getFieldDecorator('expenseId')(
                    <Select allowClear style={{ width: '100%' }} placeholder="请选择费用类型" onChange={this.selectChange}>
                      {
                        this.state.selectList.length > 0 ? this.state.selectList.map(item => (
                          <Option value={item.id} key={item.id} style={{ fontSize: '14px' }}>{item.expenseName}</Option>
                        )) : ''
                      }
                    </Select>
                  )}
                </FormItem>
              </Col>
              <Col span={8} push={2}>
                <FormItem {...formItemLayout} label="模板名称">
                  {getFieldDecorator('templateName')(<Input allowClear onChange={this.inputChange} placeholder="请输入模板名称" />)}
                </FormItem>
              </Col>
              <Col span={4} push={4}>
                <div style={{ marginTop: 5 }}>
                  <Button onClick={this.handleReset} shape="round" style={{ fontSize: '14px' }}>重置</Button>
                </div>
              </Col>
            </Row>

五: table定栏滑动,scrollX算法

有时候,表格比较宽的时候,可能需要定最左栏,或者几栏,其余可以滑动的效果,坑就是这个scrollX的值是动态算出来的,不是直接写死的值,如果直接写死的,那么,到了其他屏幕上就会有空隙的情况;

render: (){
  const columns = [{
      title: '费用类型',
      dataIndex: 'expenseName',
      width: '500px',

    }, {
      title: '模板名称',
      dataIndex: 'name',
      width: '240px',
    }, {
      title: '更新人',
      dataIndex: 'updater',
      width: '240px',
    }, {
      title: '更新时间',
      width: '300px',
      render: record => (
        <span>{this.time(record.updateAt)}</span>
      ),
    }]

let scrollX = columns.reduce((sum, item, idx) => {
  // console.log('sum, item, idx--->',sum, item, idx)
  if (idx !== 0) {
    item.width = item.title.split('').length * 15 < 240
      ? 240
      : item.title.split('').length * 15
  }
  return sum + item.width
}, 0)

// 如果有某一列小于240,也按照240展示了,这样算出来的scrollX就是动态的了,
             <Table
                  rowKey={record => `${record.deptCode}`}
                  loading={tableLoading}
                  defaultExpandedRowKeys={treeData.map(item => item.deptCode)}
                  columns={columns}
                  dataSource={treeData}
                  pagination={false}
                  onExpand={this.handldOnExpand}
                  scroll={{ y: 450, x: scrollX}}
                />
}

六:下载流文件的坑:

后端返回的excal前端下载一般有两种方式,一种是返回路径:window.open打开下载,另一种是后端直接返回一种流文件,坑是本来我用的是fetch请求的,但是,headers里面不仅需要用get()

方法取出来,请求头也做了拦截转化,所以,这种情况下,还是用axios比较好,省去了一堆bug,在这点上,axios优于fetch

server里面:

import axios from 'axios';
export const downloadSummaryData = data => axios('/sst/downloadSummaryData', { // 下载
  method: 'POST', // 注意这里是post还是get,如果是get,就要在路径上拼接参数了
  data,
  responseType: 'blob', // 转化成流文件
});

页面上:

// 下载明细
  downLoadDetail = (record) => {
    this.props.appContext.adminActions.downloadDetail({ // 参数
      expenseId: record.expenseId
    })
      .then((res) => {
        const contentType = res.headers['content-type'];
        const disposition = res.headers['content-disposition'];
        const fileName = decodeURI(disposition.substring(disposition.indexOf('filename=') + 9, disposition.length));
        // console.log('fileName---', fileName, res);
        const blob = new Blob([res.data], { type: contentType });
        const link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = fileName;
        link.click();
        link.remove();
      });
  }

6853da1b89d0fae63c6b4928c03921a5.png
axios拿到的流文件信息

版权声明:本文为weixin_29605607原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。