react-umi-demo:自定义实现table组件

本文介绍了一个自定义的React表格组件的实现方式。该组件支持分页、行内点击事件等功能,并且提供了灵活的数据展示方式。文章通过具体示例展示了如何使用此组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

组件实现

src\components\Table\index.tsx文件


import React, { useMemo, useState } from 'react';
import './index.less';


type NodeType = string | React.ReactNode | ((item?: any) => React.ReactNode);

interface HeaderItem {

  /* 列key */
  key: string;

  /* 渲染头部 */
  header: NodeType;

  /* 渲染数据 */
  render?: (item: any) => React.ReactNode;


}


interface TableProps {

  /* 列表数据 */
  data: any[];

  /* 头部数据 */
  column: HeaderItem[];

  /* 列表数量 */
  total: number;

  /* 当前页码 */
  pageIndex?: number;

  /* 当前页显示条数 */
  pageSize?: number;

  /* 页条数数组 */
  sizeList?: number[];

  /* 样式 */
  style?: React.CSSProperties;

  /* 类名 */
  className?: string;

  /* 行内点击事件 */
  handleRow?: React.MouseEventHandler;

  /* 分页 */
  onChangePage?: (pageIndex: number, pageSize: number) => void;

  /* 排序 */
  handleSort?: (key: string, sort: 'desc' | 'asd' | '') => void;
}

export default function Table(props: TableProps) {

  const {
    data,
    column,
    total,
    style,
    className,
    pageIndex,
    pageSize,
    sizeList,
    onChangePage,
  } = props;

  // 当前页
  const [currentPageIndex, setCurrentPageIndex] = useState(pageIndex || 1);
  // 当前页码显示数量
  const [currentPageSize, setCurrentPageSize] = useState(pageSize || 5);

  // 计算当前页数
  const pageCount = (total: number, size: number) => {
    return (total / size).toString().includes('.') ? Math.floor(total / size) + 1 : Math.floor(total / size)
  }

  // 总页码数
  const [page, setPage] = useState(pageCount(total, currentPageSize));
  // console.log('page', page, currentPageIndex);

  // 表头渲染
  const renderOrderByType = (content: NodeType) => {
    let type = 'string';
    let result = content;
    if (typeof content === 'string') {
      type = 'string';
      result = content;
    } else if (content instanceof Function) {
      type = 'function';
      result = content();
    } else {
      type = 'element';
      result = content;
    }
    return result;
  }

  // 数据渲染逻辑
  const renderDataItemType = (key: string, dataItem: any, render?: (item: any) => React.ReactNode) => {
    if (render === undefined) {
      return dataItem[key]
    } else {
      return render(dataItem)
    }
  }

  // 渲染表头
  const renderColumn: JSX.Element = useMemo(() => {
    return (
      <>
        <tr>
          {
            column?.map((item, index: number) => {
              return (
                <React.Fragment key={index}>
                  <th >
                    {renderOrderByType(item.header)}
                  </th>
                </React.Fragment>
              )
            })
          }
        </tr>
      </>
    )
  }, [column])

  // 列表数据渲染
  const renderTableList: JSX.Element = useMemo(() => {
    return (
      <>

        {
          data?.map((dataItem, dataIndex: number) => {
            return <React.Fragment key={dataIndex}>
              <tr>
                {
                  column?.map((colItem, colIndex) => {
                    return (
                      <React.Fragment key={colIndex}>
                        <td >
                          {renderDataItemType(colItem.key, dataItem, colItem.render)}
                        </td>
                      </React.Fragment>
                    )
                  })
                }
              </tr>
            </React.Fragment>
          })
        }
      </>
    )
  }, [data, column])


  // 切换上一页下一页或每页条数
  const handlePage = (type: string, value?: number) => {

    let pageIndex = currentPageIndex;
    let pageSize = currentPageSize;

    switch (type) {
      case 'pre':
        pageIndex = pageIndex -1
        setCurrentPageIndex(pageIndex);
        break;
      case 'next':
        pageIndex = pageIndex + 1
        setCurrentPageIndex(pageIndex);
        break;
      case 'itemCount':
        pageIndex = 1;
        pageSize = Number(value) || currentPageSize;
        setCurrentPageIndex(1);
        setCurrentPageSize(pageSize)
        setPage(pageCount(total, pageSize));

        break;
    }

    onChangePage && onChangePage?.(pageIndex, pageSize)
  }

  return (
    <div className='H-Table'>
      <table className='H-Table-inner'>
        <thead className='H-Table-header'>
          {renderColumn}
        </thead>
        <tbody className='H-Table-body'>
          {
            renderTableList
          }
        </tbody>
      </table>
      <div className='H-Table-page'>
        <span className='show'>{total}</span>
        <div className='operation'>
          <select className=' item' onChange={(e) =>{
             handlePage('itemCount',Number(e.target.value))
          }}>
            <option value="5">5</option>
            <option value="10">10</option>
          </select>
          <button
            className={`${currentPageIndex === 1 ? 'disabled-item item' : 'item'}`}
            onClick={() => handlePage('pre')}
            disabled={currentPageIndex === 1}
          >
            上一个
          </button>
          <button
            className={`${page === currentPageIndex ? 'disabled-item item' : 'item'}`}
            onClick={() => handlePage('next')}
            disabled={page === currentPageIndex}
          >
            下一页
          </button>
        </div>

      </div>
    </div>
  )
}

src\components\Table\index.less文件


.H-Table {
  text-align: left;

  &-inner {
    width: 100%;

  }

  &-header {
    border-bottom: 1px solid #e7eaef;

    th {
      height: 46px;
    }
  }

  &-body {
    td {
      height: 42px;
      box-sizing: content-box;
    }
  }

  &-page {
    border-top: 1px solid #e7eaef;
    height: 60px;
    line-height: 60px;
    display: flex;
    justify-content: space-between;
    margin: auto 20px;

    .item {
      margin-left: 10px;
      height: 40px;
      line-height: 30px;
      border: 1px solid #e7eaef;
      padding: 4px;
      cursor: pointer;
    }

    .disabled-item {
      cursor: not-allowed;
    }
  }

}

组件使用
src\pages\index.tsx文件


import React, { useState, useEffect, useRef } from 'react';
import './index.less';

import Table from '@/components/Table';

const dataList = [
  {
    id: '1',
    count: 30,
    rank: 98
  },
  {
    id: '2',
    count: 30,
    rank: 98
  },
  {
    id: '3',
    count: 30,
    rank: 98
  },
  {
    id: '4',
    count: 30,
    rank: 98
  },
  {
    id: '5',
    count: 30,
    rank: 98
  },
  {
    id: '6',
    count: 30,
    rank: 98
  },
  {
    id: '7',
    count: 30,
    rank: 98
  },
  {
    id: '8',
    count: 30,
    rank: 98
  },
  {
    id: '9',
    count: 30,
    rank: 98
  },
  {
    id: '10',
    count: 30,
    rank: 98
  },
  {
    id: '11',
    count: 30,
    rank: 98
  },
  {
    id: '12',
    count: 30,
    rank: 98
  }
]


const ColumnList = [
  {
    key: 'id',
    header: 'ID'
  },
  {
    key: 'count',
    header: () => {
      return (
        <span>Count</span>
      )
    },
    render: (ctx) => {
      return <span>{`${ctx.count}`}</span>
    }
  }, {
    key: 'rank',
    header: <span>Rank</span>,
    render: (ctx) => {
      return (
        <>
          <p>{`${ctx.rank}级别`}</p>
          <div>详细查看</div>
        </>
      )
    }
  }

]
export default function IndexPage() {

  const [tableList, setTableList] = useState(dataList.slice(0, 5))

  return (
    <div className="indexPage" >

      <div>
        <Table
          data={tableList}
          column={ColumnList}
          total={12}
          onChangePage={(pageIndex: number, pageSize: number) => {
            console.log('onChangePage', pageIndex, pageSize);
            setTableList(dataList.slice((pageIndex - 1) * pageSize, pageIndex * pageSize))
          }}
        />
      </div>
    </div>
  );
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值