使用antd封装一个table组件

100 阅读2分钟

首先说一下,antd封装的table组件已经很方便了,为什么还要封装table呢?在我们做项目时肯定不是一个人,而是一个团队,每个人都有自己的风格,总不能每次都在项目中或者去antd官网拷贝一个table吧,有些公共的部分,比如分页,时间久了,很难保证拷贝的一样如果没有特殊要求,一个系统能保持一致最好。

下面我就我在项目中使用table的经验总结了此篇文章,我顺便也加入了动态列,方便以后自己拿来即用,如果能帮助到其他人那真的是再好不过了(本代码基于antd V5)。

先看一下效果图:

先说一下封装思路,对于一个table来说可分为表头、数据、分页,对于边框、每页显示多少条、总数、跳转这些功能都是很少改变的,所以最好把这些封装到组件中,如果哪一天突然想改变了,是不是只需要把组件改一下,是不是整个系统都改了,同时还支持外面传入,支持个别页面定制化。

demo目录结构如下:

组件的封装

src\components\table\index.js

  • 通过pagination控制是否显示分页,三个参数currentpageSizetotal
  • resProps是除去columnsdataSourcepaginationcolumnsKey这四个props后的其他传入的props参数;
  • columnsKey是控制右上角是否显示自动列功能,同时还是在localStorage存储自动列的key,存储的是columns中的dataIndex值,所以columns中的dataIndex不能重复;
  • 进入页面后,监听columns的变化,先去localStorage中取值,如果有值就筛选出columns,如果没值就直接用columns
// index.js
import React, { useEffect, useState } from 'react'
import { Table } from 'antd'
import AntdTableOptPage from './AntdTableOpt'
const AntdTablePage = props => {
  // columnsKey,独一无二的表示,不能重复
  const { columns, dataSource, pagination, columnsKey, ...resProps } = props
  const [tableColumns, setTableColumns] = useState(columns || [])
  useEffect(() => {
    initFun()
  }, [columns])
  const initFun = () => {
    let localColumns = columnsKey && localStorage.getItem(columnsKey)
    if (localColumns) {
      try {
        let localColumnsJson = JSON.parse(localColumns)
        // 设置table列
        initTableColumns(localColumnsJson)
      } catch (error) {
        // 设置table列
        initTableColumns([], 'init')
      }
    } else {
      // 设置table列
      initTableColumns([], 'init')
    }
  }
  // 设置table列
  const initTableColumns = (ls, type) => {
    let newCol = []
    for (let index = 0; index < columns.length; index++) {
      const element = columns[index]
      if (!element.dataIndex) {
        newCol.push(element)
      } else {
        if (type === 'init') {
          newCol.push(element)
        } else {
          if (ls.includes(element.dataIndex)) {
            newCol.push(element)
          }
        }
      }
    }
    if (!type) {
      // 存储table列
      columnsKey && localStorage.setItem(columnsKey, JSON.stringify(ls))
    }
    // 设置table列
    setTableColumns(newCol)
  }
  return (
    <div>
      {columnsKey && (
        <AntdTableOptPage
          columnsChange={newColumns => {
            initTableColumns(newColumns)
          }}
          tableColumns={tableColumns}
          columns={columns.filter(itemMap => itemMap.dataIndex)}
        />
      )}
      <Table
        size="small"
        dataSource={dataSource.map((data, index) => {
          return { ...data, key: index }
        })}
        bordered
        columns={tableColumns}
        pagination={
          pagination
            ? {
              current: pagination.current,
              pageSize: pagination.pageSize,
              total: pagination.total,
              showSizeChanger: true,
              showQuickJumper: true,
              pageSizeOptions: [10, 20, 50, 100],
              showTotal: total => `total ${total}`
            }
            : false
        }
        {...resProps}
      />
    </div>
  )
}
export default AntdTablePage

src\components\table\AntdTableOpt.js

// AntdTableOpt.js
import React, { useEffect, useState } from 'react'
import { Popover, Checkbox } from 'antd'
import { SettingOutlined } from '@ant-design/icons'
const AntdTableOptPage = props => {

  const { columns, tableColumns, columnsChange } = props
  // 操作后的columns
  const defaultV = tableColumns
    .map(itemMap => {
      return itemMap.dataIndex
    })
    .filter(Boolean)
  // 原始columns
  const allDataIndex = columns
    .map(item => {
      return item.dataIndex
    })
    .filter(Boolean)
  const [checkAll, setCheckAll] = useState(true)
  useEffect(() => {
    let defCheckAll = defaultV.length === allDataIndex.length ? true : false
    setCheckAll(defCheckAll)
  }, [tableColumns])
  const content = (
    <div>
      <Checkbox.Group
        style={{
          width: '100%',
          display: 'block',
          maxHeightheight: '300px',
          overflow: 'auto'
        }}
        value={defaultV}
        onChange={value => {
          columnsChange && columnsChange(value)
        }}
      >
        {columns.map((item, idx) => {
          return (
            <div
              style={{
                padding: '5px 10px'
              }}
              key={idx}
            >
              <Checkbox value={item.dataIndex}>{item.title}</Checkbox>
            </div>
          )
        })}
      </Checkbox.Group>
    </div>
  )

  const onCheckAllChange = () => {
    let checkA = !checkAll
    let dtIdxs = []
    if (!checkAll) {
      dtIdxs = allDataIndex
    }
    setCheckAll(checkA)
    columnsChange && columnsChange(dtIdxs)
  }
  const contentSelAll = (
    <div
      style={{
        padding: '5px 10px 10px',
        borderBottom: '1px solid #ccc'
      }}
    >
      <Checkbox
        checked={checkAll}
        onChange={() => {
          onCheckAllChange()
        }}
      >
        全选
      </Checkbox>
    </div>
  )
  return (
    <div style={{
      textAlign: 'right',
      padding: '10px',
    }}>
      <Popover placement="leftBottom" content={content} title={contentSelAll} trigger="click">
        <SettingOutlined />
      </Popover>
    </div>
  )
}
export default AntdTableOptPage


src\App.js

import React, { useEffect, useState } from 'react'
import AntdTable from './components/table';
import './App.css';

const App = () => {
  const paginationInit = {
    current: 1,
    pageSize: 10,
    total: 0
  }
  const [queryFlag, setQueryFlag] = useState(false)
  const [pagination, setPagination] = useState(paginationInit)
  const [tableList, setTableList] = useState([])
  useEffect(() => {
    searchDataFun()
  }, [queryFlag])
  const tableColumns = [
    {
      title: 'Full Name',
      dataIndex: 'name',
      key: 'name'
    },
    {
      title: 'Age',
      dataIndex: 'age',
      key: 'age'
    },
    {
      title: 'Column 1',
      dataIndex: 'address1',
      key: '1',
    },
    {
      title: 'Column 2',
      dataIndex: 'address2',
      key: '2',
    },
    {
      title: 'Column 3',
      dataIndex: 'address3',
      key: '3',
    },
    {
      title: 'Column 4',
      dataIndex: 'address4',
      key: '4',
    },
    {
      title: 'Column 5',
      dataIndex: 'address5',
      key: '5',
    },
    {
      title: 'Column 6',
      dataIndex: 'address6',
      key: '6',
    },
    {
      title: 'Column 7',
      dataIndex: 'address7',
      key: '7',
    },
    {
      title: 'Column 8',
      dataIndex: 'address8',
      key: '8',
    }
  ]

  const getData = () => {
    let tableData = [];
    let start = (pagination.current - 1) * pagination.pageSize
    let end = (pagination.current) * pagination.pageSize
    for (let i = start; i < end; i++) {
      tableData.push({
        key: i,
        name: `Edward ${i}`,
        age: 32,
        address1: `London Park no. ${i}`,
        address2: `London Park no. ${i}`,
        address3: `London Park no. ${i}`,
        address4: `London Park no. ${i}`,
        address5: `London Park no. ${i}`,
        address6: `London Park no. ${i}`,
        address7: `London Park no. ${i}`,
        address8: `London Park no. ${i}`,
      });
    }
    return { records: tableData, total: 100 }
  }

  const searchDataFun = () => {
    setTimeout(() => {
      let res = getData()
      setTableList(res.records)
      const pg = {
        ...pagination,
        total: res.total
      }
      setPagination(pg)
    }, 500)

  }
  // const searchClearDataBtn = () => {
  //   const pg = {
  //     ...pagination,
  //     current: 1
  //   }
  //   setPagination(pg)
  //   setQueryFlag(!queryFlag)
  // }
  const handleTableChange = pagina => {
    const params = {
      ...pagination,
      current: pagina.current,
      pageSize: pagina.pageSize
    }
    setPagination({ ...params })
    setQueryFlag(!queryFlag)
  }
  return (
    <div className="App">
      <AntdTable
        dataSource={tableList}
        columns={tableColumns}
        // 独一无二的表示不能重复
        columnsKey="tableLocalColumns"
        pagination={pagination}
        onChange={handleTableChange}
      />

    </div>
  );
}

export default App;