react-umi-demo: 自定义实现Table组件支持自定义单字段或多字段排序

简略实现了表头排序标志,排序逻辑需要使用组件时进行自定义排序逻辑,处理好数据再传入组件
存在问题:已排序的前提下,再分页后的数据未进行排序

组件实现
src\components\Table\type.ts文件

export interface sortItem {
  key: string;
  type: '' | 'asc' | 'desc'
}
export interface sortValueItem {
  '': 'desc',
  desc: 'asc',
  asc: 'desc'
}
export const sortRule: sortValueItem = {
  '': 'desc',
  desc: 'asc',
  asc: 'desc'
}

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

export interface HeaderItem {

  /* 列key */
  key: string;

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

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

  /* table组件自动排序*/
  sort?: boolean;


}


export interface TableProps {

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

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

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

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

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

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

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

  /* 类名 */
  className?: string;

  sortType?: 'double' | 'single';

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

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

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

src\components\Table\index.tsx文件


import React, { useMemo, useState } from 'react';
import './index.less';
import {
  sortItem,
  TableProps,
  sortRule,
  sortValueItem,
  HeaderItem,
  NodeType,
} from './type';

export default function Table(props: TableProps) {

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

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

  // 排序相关信息
  const sortList = (column?.filter(v => v?.sort === true))?.map(item => {
    let objItem: sortItem = { type: '', key: item.key }
    return objItem
  });
  const [sortInfo, setSortInfo] = useState<sortItem[]>(sortList);
  // 计算当前页数
  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 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)
  }

  // 排序标志逻辑
  const sortFlag = (type: string) => {

    switch (type) {
      case 'desc':
        return '▽';
      case 'asc':
        return '△'
      default:
        return '☆'
    }
  }

  const sortFun = (item) => {
    // 优化版
    let newSort = sortInfo?.map((v: sortItem) => {
      if (v.key === item.key) {
        // 找出当前排序字段,切换成下个状态
        v.type = sortRule[v?.type];
      } else {
        // 判断是多字段排序还是当字段排序,单字段排序需将其他字段排序type设置''
        if (sortType === 'single') {
          v.type = ''
        }
      }
      return v;
    })
    setSortInfo(newSort);
    handleSort?.(item.key, sortInfo?.filter(v => v.key === item.key)[0]?.type, newSort);

  }
  // 表头渲染
  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)}
                    {/* 排序标志 */}
                    {
                      item?.sort && (
                        <button onClick={() => sortFun(item)}>
                          {
                            sortFlag(sortInfo.filter(v => v.key === item.key)[0]?.type)
                          }
                        </button>
                      )
                    }
                  </th>
                </React.Fragment>
              )
            })
          }
        </tr>
      </>
    )
  }, [column, sortInfo])

  // 列表数据渲染
  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])




  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>
            <option value="20">20</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\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',
    sort: true,
  },
  {
    key: 'count',
    header: () => {
      return (
        <span>Count</span>
      )
    },
    render: (ctx) => {
      return <span>{`${ctx.count}`}</span>
    },
    sort: true
  }, {
    key: 'rank',
    header: <span>Rank</span>,
    render: (ctx) => {
      return (
        <>
          <p>{`${ctx.rank}级别`}</p>
          <div>详细查看</div>
        </>
      )
    },
    sort: true
  }, {
    key: 'remark',
    header: 'remark'
  }

]
export default function IndexPage() {

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

  return (
    <div className="indexPage" >

      <div>
        <Table
          data={tableList}
          column={ColumnList}
          total={12}
          sortType={'single'}
          onChangePage={(pageIndex: number, pageSize: number) => {
            let newList = dataList.slice((pageIndex - 1) * pageSize, pageIndex * pageSize)
            setTableList(newList)
            listRef.current = newList
          }}
          handleSort={(key, type, sortItem) => {
            console.log('handleSort', tableList, listRef.current);
            if (key !== 'id') return;
            let newList = [...listRef.current]?.sort((a, b) => {
              switch (type) {
                case 'asc': return a[key] - b[key];
                case 'desc': return b[key] - a[key]

              }
            })
            setTableList(newList)
          }}
        />
      </div>
    </div>
  );
}

排序函数回调拿不到最新的值解决

使用useRef


export default function IndexPage() {

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

  return (
    <div className="indexPage" >

      <div>
        <Table
          data={tableList}
          column={ColumnList}
          total={12}
          sortType={'single'}
          onChangePage={(pageIndex: number, pageSize: number) => {
            let newList = dataList.slice((pageIndex - 1) * pageSize, pageIndex * pageSize)
            setTableList(newList)
            listRef.current = newList
          }}
          handleSort={(key, type, sortItem) => {
            // 打印可以看到tableList拿的是初始化的值
            console.log('handleSort', tableList, listRef.current);
            if (key !== 'id') return;
            let newList = [...tableList]?.sort((a, b) => {
              switch (type) {
                case 'asc': return a[key].localeCompare(b[key]);
                case 'desc': return b[key].localeCompare(a[key])

              }
            })
            setTableList(newList)
          }}
        />
      </div>
    </div>
  );
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值