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