react+antd封装表格组件2.0

react+antd封装表格组件2.0

1.0版本 仅仅封装组件,不涉及方法

1.0 仅封装组件

此方法把所用方法集体封装,以后就可以无脑开发拉!

  • 只需传入路径,组件列表请求、增删改后更新属性均在组件内发生
  • 当前页不是第一页且删除后无数据,则自动跳转到上一页
  • 查询时防止多次点击,点击一次后加遮罩,数据返回后放开
  • 已有查询条件时,再次分页带上查询条件

需要掌握知识点

useImperativeHandle

一个用于暴露自定义ref属性和自定义方法的钩子函数。可以使得父组件可以通过ref访问子组件中定义的方法和属性,从而实现对子组件的精细控制

!!使用useImperativeHandle时必须与forwardRef搭配使用,否则会报错

  • 具体使用步骤如下
javascript 复制代码
//在子组件中定义需要暴露给父组件的方法和属性,并将这些方法和属性放在一个对象中
import { useImperativeHandle, forwardRef } from 'react';

const Child = forwardRef((props, ref) => {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  // 定义需要暴露给父组件的方法和属性
  useImperativeHandle(ref, () => ({
    increment,
    count,
  }));

  return <div>{count}</div>;
});
javascript 复制代码
//在父组件中使用子组件,并给子组件传递一个ref属性,
import { useRef } from 'react';
import Child from './Child';

const Parent = () => {
  // 创建一个ref
  const childRef = useRef();

  // 在父组件中通过ref访问子组件的方法和属性
  const handleClick = () => {
    childRef.current.increment();
  };

  return (
    <div>
      <button onClick={handleClick}>Click me</button>
      <Child ref={childRef} />
    </div>
  );
};
//父组件中的handleClick方法通过childRef.current访问了Child组件中的increment方法和count属性。

组件代码

javascript 复制代码
import { Table, Pagination, Button, Dropdown, Checkbox, message } from 'antd';
import { useDispatch } from 'umi';
import { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
import { PicRightOutlined } from '@ant-design/icons';

import './index.less';

const TableComponent = forwardRef((props, ref) => {
  const dispatch = useDispatch();
  const [open, setOpen] = useState(false); //动态控制列的下拉显隐
  const [items, setItems] = useState([]); //当前的下拉选择状态
  const [loading, setLoading] = useState(true); //当前的加载状态
  const [dataSource, setDataSource] = useState([]); //列表数据

  const [total, setTotal] = useState(0); //分页总数
  const [size, setSize] = useState(10); //分页页数
  const [current, setCurrent] = useState(1); //分页当前页


  // 抛出获取列表方法
  useImperativeHandle(ref, () => ({
    getList,
  }));

  // 表格列表
  const [columns, setColumns] = useState(
    props.columns.map((item) => {
      return {
        ...item,
        align: 'center',
        ellipsis: {
          showTitle: false,
        },
      };
    }),
  );

  //控制列的初始值
  const [starCol, setStarCol] = useState(
    props.columns.map((item) => {
      return {
        ...item,
        check: true,
        align: 'center',
        ellipsis: {
          showTitle: false,
        },
      };
    }),
  );

  useEffect(() => {
    changItem(starCol);
  }, [props]);



  useEffect(() => {
    if (props.columns || props.columns.length > 0) {
      setColumns(
        props.columns.map((item) => {
          return {
            ...item,
            align: 'center',
            ellipsis: {
              showTitle: false,
            },
          };
        }),
      );
      setStarCol(
        props.columns.map((item) => {
          return {
            ...item,
            check: true,
            align: 'center',
            ellipsis: {
              showTitle: false,
            },
          };
        }),
      );
    }
  }, [props.columns]);


  useEffect(() => {
    // 初始请求列表
    if (props.url) {
      getList();
    }
  }, [props.url]);
  
  useEffect(() => {
    // 新增编辑后列表刷新
    if (props.list) {
      const list = props.list?.map((item, index) => {
        return {
          index: serialNumber(current, size, index + 1),
          ...item,
        };
      });
      setDataSource(list);
    }
  }, [props.list]);

  useEffect(() => {
    if (props.search) {
      dispatch({
        type: 'utils/searchLoadingEvent',
        payload: true,
      });
      // 搜索请求列表
      getList(1, size, props.search, true);
    }
  }, [props.search]); //监听列表传入查询条件

  const getList = (pageCurrent, pageSize, values, isClickSearch) => {
    if (!isClickSearch) {
      // 获取列表时 没点击查询按钮 加载列表
      setLoading(true);
    }
    const payload = {
      current: pageCurrent || current,
      size: pageSize || size,
      ...values,
    };
    dispatch({
      type: props.url,
      payload: payload,
      callback: (res) => {
        if (res) {
          setTotal(res.total);
          setSize(res.size);
          setCurrent(res.current);
          const list = res.data?.map((item, index) => {
            return {
              index: serialNumber(res.current, res.size, index + 1),
              ...item,
            };
          });
          setDataSource(list);
          setLoading(false);
          props.listPaginationChange(payload); //更新接口条件
          
          // 处理列表最后一条数据删除时请求上一页数据
          if (res.data?.length == 0 && res.total > 0) {
            getList(res.current - 1, res.size, props.search);
          }
          
          //点击查询按钮 获取到数据后 关闭局部加载
          if (isClickSearch) {
            dispatch({
              type: 'utils/searchLoadingEvent',
              payload: false,
            });
          }
        }
      },
    });
  };

  // 分页查询列表
  const paginationChange = (pageCurrent, pageSize) => {
    getList(pageCurrent, pageSize, props.search || null);
  };

//这个方法可加可不加,是因为前端需要展示序号,而后端不返回,所以就自己计算啦!
  const serialNumber = (pageIndex, pageSize, index) => {
    return (pageIndex - 1) * pageSize + index;
    
  };

  // 控制显示列的操作
  const onClick = ({ key }) => {
    if (key == 'all') {
      const newDrop = starCol;
      newDrop.map((o, index) => {
        o.check = true;
      });
      changItem(starCol);
      setColumns(
        props.columns.map((item) => {
          return {
            ...item,
            check: true,
            align: 'center',
            ellipsis: {
              showTitle: false,
            },
          };
        }),
      );
    } else {
      const newDrop = starCol;
      newDrop.map((o, index) => {
        if (index == key) {
          o.check = !o.check;
        }
      });
      let newColumns = newDrop.filter((o) => o.check);
      if (newColumns.length == 0) {
        message.warning('请至少选择一列');
      } else {
        setStarCol(newDrop);
        changItem(newDrop);
        setColumns(newColumns); //列状态
      }
    }
  };

  // 列的选中状态改变
  const changItem = (data) => {
    let opitems = data.map((item, index) => {
      return {
        key: index,
        label: <Checkbox checked={item.check}>{item.title}</Checkbox>,
      };
    });
    opitems.unshift(
      {
        key: 'all',
        label: <Button>{'全选列'}</Button>,
      },
      {
        type: 'divider',
      },
    );
    setItems(opitems); //当前的下拉状态
  };

  return (
    <div className="table">
      {!props?.selectIf && (
        <Dropdown
          menu={{
            items,
            onClick,
          }}
          overlayClassName="drop"
          trigger={['click']}
          onOpenChange={() => setOpen(!open)}
          open={open}
          arrow
          placement="bottomRight"
        >
          <a onClick={(e) => e.preventDefault()}>
            <Button icon={<PicRightOutlined />} title="显示/隐藏列"></Button>
          </a>
        </Dropdown>
      )}

      <Table
        bordered
        columns={columns}
        rowSelection={props?.rowSelection}
        // dataSource={isShow ? dataSource : null}
        dataSource={dataSource}
        rowKey={(item) => item.id || item.categoryId || item.modelId}
        pagination={false}
        className="insiadeTable"
        scroll={{
          y: 450,
          ...props?.scroll,
        }}
        onRow={props?.onRow}
        rowClassName={props?.rowClassName}
        summary={props?.summary}
        loading={loading}
      />
      <div className="pagination">
        <Pagination
          showQuickJumper
          defaultCurrent={current}
          total={total}
          showTotal={(total) => `共 ${total} 条`}
          size={size}
          current={current}
          onChange={paginationChange}
          pageSizeOptions={[10, 50, 100, 500]}
        />
      </div>
    </div>
  );
});

export default TableComponent;

引用

javascript 复制代码
···
  const [search, setSearch] = useState(null); //搜索条件
  const tableComponentRef = useRef(null);
  const list = useSelector((state) =>state.roleManagement.roleManagementList);
  const [search, setSearch] = useState(null);
  const [listChange, setListChange] = useState(list);
  const [listPaginationChange, setListPaginationChange] = useState(null);
···
  useEffect(() => {
    if (list) {
      setListChange(list); //组件内数据响应后此处更新
    }
  }, [list]);
···
  const onFinish = (values) => {
    setSearch(values); //点击搜索后设置值
  };

···
  <Form onFinish={onFinish} form={form} requiredMark={'Hidden'}>
        <Row gutter={16}>
          <Col span={6}>
            <Form.Item
              name="keyword"
              label="关键字"
            >
              <Input allowClear />
            </Form.Item>
          </Col>
          <Col span={4}>
            <Space>
              <Button
                type="primary"
                icon={<SearchOutlined />}
                htmlType="submit"
              >
                搜索
              </Button>
              <Button
                icon={<SyncOutlined />}
                onClick={() => {
                  form.resetFields();
                }}
              >
                重置
              </Button>
            </Space>
          </Col>
        </Row>
      </Form>
  
     <TableComponent
        ref={tableComponentRef} //组件实例
        url="roleManagement/getRoleManagementList"
        search={search} //搜索条件
        list={listChange} //数据源
        listPaginationChange={(e) => {
        //{current:1,size:10,..查询条件} 格式
          setListPaginationChange(e);
        }}
        className="list"
        columns={columns} //列
        scroll={{
          x: 1600,
        }}
      />
  • 新增修改组件
javascript 复制代码
      <Edit
        fillingForm={fillingForm}
        open={isModalOpen}
        setIsModalOpen={setIsModalOpen}
        //修改新增组件只需传入listPaginationChange,便可保留分页及查询条件
        listPaginationChange={listPaginationChange}
      />
     
     //修改新增后调用即可 
    const getRoleList = () => {
     dispatch({
      type: 'roleManagement/getRoleManagementList',
      payload: props.listPaginationChange,
   };
相关推荐
Black蜡笔小新1 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html
秦jh_2 小时前
【Linux】多线程(概念,控制)
linux·运维·前端
蜗牛快跑2132 小时前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy2 小时前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js
涔溪3 小时前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
榴莲千丞3 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-3 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与3 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun3 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇3 小时前
ES6进阶知识一
前端·ecmascript·es6