【React-admin】构建React应用(4)-基于antd封装查询表单分页

1.前言

之前用过antd.pro框架来实现前端开发,不管是v4还是v5都封装的不错,比如今天要说到的表格。

在日常开发中,少不了增删改查,有了这些东西,就不得出现三个必要的组件

  • 表格组件
  • 查询组件
  • 分页组件

而,antd.pro怎封装了一套表格组件,包含上面三个组件。至于为什么不直接把antd.pro中的表格组件直接拿来用呢?

在前段时间研究微前端的时候,发现antd.pro微前端的支持并不友好,为了以后方便管理前端项目,微前端是迟早要用的。antd.pro微前端支持不友好的原因是:微前端在管理子模块的时候,需要子模块在主入口文件中对外(微前端)暴露自己的生命周期函数,这样微前端框架才能获取到当前的应用生命周期

antd.pro的缺点就是封装的太彻底,以至于我们在使用的时候不需要关心他的主入口文件,它会动态生成主入口文件。这就导致我们无法找到应用的入口文件,也就无法对外暴露生命周期函数。

基于此,不得不更换为其他的框架。由于我们要做的项目跟权限管理有关,所以就采用了react-admin。而react-admin也封装了很多优秀的组件,但时预留了webpack主入口文件的配置,所以以后往微前端上集成的时候也比较方便。

然而,有一些通用的组件还是没有做好封装,比如对表格的分装的就不够好。那么在所有的页面上都会有大量重复性的表格出现,而且表格跟按钮组的联动也会重复出现在很多模块中,这就导致代码看起来非常的low。

比如下面这个表格相关的操作:
在这里插入图片描述
一共可以分为四个部分:

  1. 查询表单
  2. button按钮组
  3. 表格
  4. 分页

这四部分如果你不进行封装,就可以想到每个模块都会出现大量重复性的代码。作为一个有洁癖的程序员,奥不,应该说程序员都有洁癖。对于这样的代码是不能容忍的。

所以就着手做一下封装。

2.Table组件封装

2.1 组件封装

Q:什么叫组件封装呢?
A:其实封装跟抽象意思很相近,将有相同属性的事物统一封装到一个对象中或者组件中,以便在受益于大众。
Q:组件的思想是什么呢?
A:组件的核心应该是通用,而不是定制。
Q:什么是通用?什么是定制?有什么不一样么?
A:通用就是所有的人都能用,定制就是私人化。我们简单的抽象出一个组件,每个人都有鼻子有眼,能哭,能跑,能笑等,这些应该是个人都会有的。但拿一件事来说,你比如跑这个动作,既然你作为人组件的一个方法,那么针对不同的人你需要进行不同的操作,比如有的人跑得快,有的人跑的不快,有的人出国车祸截肢了,他不会跑。这些情况你都需要考虑到,但是你需要讲这些都在组件中进行处理么?
很显然不是!!!!
我们需要对外暴露组件的属性,然而通用组件的封装应该是有共性的,共性就是所有调用它的外部组件都有的属性,而不是针对每个组件去单独处理其特殊的地方。我们需要做的就是暴露出去共性,至于特性,那么交给调用组件者自己去处理这个问题。

起初我在做组件封装的时候一直困在tablebutton按钮组之间的联动问题上。因为不同的模块,按钮组是不同的,比如用户模块,他有增删改查禁,而日志模块只有查看和删除。而不同的按钮有不同的处理逻辑。

  • 比如说删除按钮的逻辑应该是表单是否勾选,更复杂的可能是选中的数据是否被其他模块引用
  • 比如启用/禁用按钮,只有当选中的数据是启用状态时才能禁用,禁用的数据也只能启用

为了方便table和按钮组的联动问题,我一开始是将按钮组放到组件中,将所有的按钮种类罗列了一遍,然后根据父组件传递的权限爱控制按钮的显示隐藏。功能也能实现,但是看起来额外的别扭,因为不同的按钮有不同的处理逻辑,这样的话所有的处理逻辑都会在子组件中进行处理,那么这就成了我们前面所说的定制/特有而不是通用。所有想了再三,最后还是将button按钮组的定义放到了外面。

2.2 定义组件

思考下,如果我们需要封装一个组件,那么我们需要怎么做?
我们封装一个表格组件,那么组件中包含:

  • 能够生成搜索表单的属性
  • 能够生成表格的属性
  • 能够生成按钮组的属性
  • 能够生成分页的属性
  • 能够回调的函数

有了上面的思考,我们就有了想法。我们首先看一下表格,因为所有的操作都是依赖于表格的,不管是查询还是分页,不管是增删改,都离不开表格,所以只要我们需要把表格组件的属性搞明白,那么其他的部分就自然而然的小意思了。

2.2.1 表格的封装

由于我们还是基于react+antd,所以最基本的还是要基于antd的table组件

一个table的组件应当包含最基本的两部分:

  • 数据源DataSource
  • 列columns

数据源是通过接口向后台请求来的,column是根据后台返回数据的属性定义的。所以我们就需要定义这两个部分

const columns = [
      {
    title: '部门名称', dataIndex: 'name', type: 'input' },
      {
   
        title: '上级部门', dataIndex: 'parent_name', type: 'select-tree',
        options: departments, style: {
    width: "250px" },
        render: (value) => {
   
          return value ? <div styleName="tags-style">{
   value}</div> : <Tag color="#f50"></Tag>
        }
      },
      {
   
        title: '创建时间', dataIndex: 'createtime', type: 'date-range',
        render: (value) => {
   
          return dateFormat(value * 1000)
        }
      },
      {
   
        title: '修改时间', dataIndex: 'modifytime', type: 'date-range',
        render: (value) => {
   
          return dateFormat(value * 1000)
        }
      },
      {
   
        title: '状态', dataIndex: 'status', type: "radio-group",
        options: [{
    label: '启用', value: 'enabled' }, {
    label: '禁用', value: 'disabled' }],
        render: (value) => {
   
          if (value === "enabled") {
   
            return <Tag color="#108ee9">启用</Tag>
          } else {
   
            return <Tag color="#f50">禁用</Tag>
          }
        }
      },
      {
   
        title: '操作', dataIndex: 'operator',
        render: (value, record) => {
   
          const {
    id, name } = record;
          const items = []
          if (operations.detail) {
   
            items.push({
   
              label: '查看',
              icon: 'eye',
              onClick: (e) => {
   
                e.stopPropagation();
                this.setState({
    visible: true, id: record.id, record, type: 'detail', drawerTitle: '部门详情' });
              },
            })
          }
          if (operations.modify) {
   
            items.push({
   
              label: '修改',
              icon: 'form',
              onClick: (e) => {
   
                e.stopPropagation();
                this.setState({
    visible: true, id, record, type: 'edit', drawerTitle: '修改部门' });
              },
            })
          }
          if (operations.delete) {
   
            items.push({
   
              label: '删除',
              color: 'red',
              icon: 'delete',
              confirm: {
   
                title: `您确定删除"${
     name}"?`,
                onConfirm: (e) => {
   
                  e.stopPropagation();
                  this.handleDelete(id);
                },
              },
            })
          }
          return <Operator items={
   items} />;
        },
      },
    ];

那么一个表格的雏形应该是这样的

父组件:

<DynaroseTable
   loading={loading}
   columns={columns}
   dataSource={dataSource}
   total={total}
 />

子组件:

	  <Table
        loading={
   loading}
        className="components-table-demo-nested"
        rowClassName={
   record => {
   
          if (record.id === selectedRoleId) return 'role-table selected';
          return 'role-table'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值