Commit 13ee2984 by zhaochengxiang

去掉虚拟滚动

parent e186cb4f
.virtual-table {
.rdg {
border: none !important;
}
.rdg-header-row {
.rdg-cell {
box-shadow: none !important;
background-color: #f2f5fc!important;
border-block-end: none !important;
border-inline-end: none !important;
font-weight: normal !important;
outline: none !important;
top: 0 !important;
padding-inline: 0 !important;
padding-left: 8px !important;
padding-right: 8px !important;
&:before {
position: absolute;
top: 50%;
right: 0;
width: 1px;
height: 1.6em;
background-color: rgba(0,0,0,.06);
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
transition: background-color .3s;
content: "";
}
&:after {
width: 8px;
right: 0;
top: 0;
bottom: 0;
}
}
}
.rdg-row {
&:hover {
background-color: #fafafa;
}
.rdg-cell {
box-shadow: none !important;
color: #363636 !important;
border-block-end: 1px solid #f0f0f0 !important;
border-inline-end: none !important;
outline: none !important;
padding-inline: 0 !important;
padding-left: 8px !important;
padding-right: 8px !important;
}
}
}
\ No newline at end of file
import React, { useCallback, useEffect, useMemo, useState, forwardRef, useRef } from 'react';
import DataGrid, { Column, DataGridProps, Row as GridRow, RowsChangeData, SortColumn, SortIconProps, SelectColumn } from 'react-data-grid';
import type { DataGridHandle, CheckboxFormatterProps } from 'react-data-grid';
import { Checkbox, Empty } from 'antd';
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { downNode, upNode } from './virtual-table-helper';
import { paginate } from '../../../util';
import './index.less';
export enum RowAction {
None, Expand, Select
}
export enum RowType {
None, Detail
}
export interface RowData {
id: string,
index?: string;
__pid__?: string
__row__?: any
__type__?: RowType
__expanded__?: boolean
__selected__?: boolean
__action__?: RowAction
}
interface Props<Row> {
gridRef?: React.RefObject<DataGridHandle>
onContextMenu?: (event: React.MouseEvent, row: RowData) => void
checkable: Boolean
expandable?: {
expandedRowHeight?: number
rowExpandable?: (row: Row) => Boolean
expandRowRender?: (row: Row) => React.ReactElement
}
getComparator?: (sortColumn: string) => (a: Row, b: Row) => number
loadMoreRows?: (length: number) => Promise<Row[]> | undefined
selectedRows?: Array<any>
onSelectedRowsChange?: (selectedRows: Array<any>) => void
rowHeight?: number
rowClassName?: (row: RowData) => string
scrollRowIndex: number
}
const CheckboxFormatter = forwardRef<HTMLInputElement, CheckboxFormatterProps>(
function CheckboxFormatter({ onChange, ...props }: CheckboxFormatterProps, ref) {
function handleChange(e: CheckboxChangeEvent) {
onChange(e.target.checked, (e.nativeEvent as MouseEvent).shiftKey);
}
return <Checkbox ref={ref} {...props} onChange={handleChange} />;
}
);
export function getExpandingCol<Row extends RowData, SR>({ colSpan, expandRender, expandable }: {
colSpan: () => number
expandRender?: (row: Row) => React.ReactElement
expandable?: (row: Row) => Boolean
}) {
const col: Column<Row, SR> = {
key: 'expanded',
name: '',
minWidth: 30,
width: 30,
colSpan(args) {
return args.type === 'ROW' && args.row.__type__ === RowType.Detail ? colSpan() : undefined;
},
cellClass(row) {
return row.__type__ === RowType.Detail ? 'detail' : undefined;
},
formatter({ row, isCellSelected, onRowChange }) {
if (row.__type__ === RowType.Detail) {
if (expandRender) {
return expandRender(row.__row__)
}
return (
<div>{row.__pid__}</div>
);
}
if (!expandable || !expandable(row)) return null;
// 展开收起
return (
<div style={{ cursor: 'pointer', color: isCellSelected ? '#777' : '#ccc' }}
onClick={() => {
onRowChange({
...row,
__expanded__: !row.__expanded__,
__action__: RowAction.Expand
});
}}
>
{row.__expanded__ ? <UpOutlined /> : <DownOutlined />}
</div>
);
}
}
return col
}
export const defaultPageSize = 800
function FC<Row extends RowData, SR, K extends React.Key = React.Key>(props: DataGridProps<Row, SR, K> & Props<Row>) {
const {
gridRef,
expandable,
getComparator,
loadMoreRows,
onSelectedRowsChange, columns, rows, checkable, selectedRows, rowHeight = 45, rowClassName, onContextMenu, scrollRowIndex, ...rest } = props
const rowKeyGetter = (row: Row): K => {
return row.id as K;
}
// 初始化onRowsChange
const onRowsChange = useCallback((rows: RowData[], { indexes }: RowsChangeData<Row, SR>) => {
const row = rows[indexes[0]];
if (row.__action__ === RowAction.Expand) {
if (!row.__expanded__) {
rows.splice(indexes[0] + 1, 1); // 删除下一行
} else {
rows.splice(indexes[0] + 1, 0, { // 插入下一行
id: `${row.id}-detail`, __type__: RowType.Detail, __pid__: row.id, __row__: row,
});
}
} else if (row.__action__ === RowAction.Select) {
// nothing to do, just update
}
_setRows(rows as Row[]);
}, [])
// 已排序行
const [sortColumns, setSortColumns] = useState<readonly SortColumn[]>([]); // 排序列图标状态
const [_rows, _setRows] = useState<readonly Row[]>([])
const [pagination, setPagination] = useState({ pageNum: 1, pageSize: defaultPageSize });
const {pageNum, pageSize} = pagination;
const prevScrollTopRef = useRef(0)
useEffect(() => {
if (rows) {
setPagination(prev => {
return {...prev, pageNum: 1}
})
}
}, [rows])
useEffect(() => {
if (scrollRowIndex) {
setPagination(prev => {
return {...prev, pageNum: parseInt((scrollRowIndex/defaultPageSize + ((scrollRowIndex%defaultPageSize===0)?0:1)).toString())}
})
}
}, [scrollRowIndex])
useEffect(() => {
let newRows = [...rows]
if (sortColumns.length > 0) {
newRows
// .filter(item => item.__type__ !== RowType.Detail)
.sort((a, b) => {
for (const sort of sortColumns) {
const comparator = getComparator ? getComparator(sort.columnKey) : GetComparator(sort.columnKey);
const compResult = comparator(a, b);
if (compResult !== 0) {
return sort.direction === 'ASC' ? compResult : -compResult;
}
}
return 0;
})
}
newRows = paginate(newRows, pageNum, pageSize);
(newRows||[]).forEach((item, index) => {
item.index = `${(pageNum-1)*pageSize+index+1}`;
})
_setRows(newRows);
}, [sortColumns, getComparator, pagination]);
// 组装功能s列
const cols = useMemo(() => {
if (expandable && checkable) {
const cols: readonly Column<Row, SR>[] = [
getExpandingCol<Row, SR>({
colSpan: () => columns.length+2, expandRender: expandable.expandRowRender, expandable: expandable.rowExpandable
}),
{ ...SelectColumn, key: 'select', frozen: false },
...columns,
]
return cols
} else if (expandable) {
const cols: readonly Column<Row, SR>[] = [
getExpandingCol<Row, SR>({
colSpan: () => columns.length+1, expandRender: expandable.expandRowRender, expandable: expandable.rowExpandable
}),
...columns,
]
return cols
} else if (checkable) {
const cols: readonly Column<Row, SR>[] = [
{ ...SelectColumn, key: 'select', frozen: false },
...columns,
]
return cols
}
return columns
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [columns, expandable, checkable])
// 处理滚动
const handleScroll = useCallback((event: React.UIEvent<HTMLDivElement>) => {
if (isAtTop(event) && prevScrollTopRef.current > event.currentTarget.scrollTop) {
if (pageNum > 1) {
setPagination(prev => {
return {...prev, pageNum: prev.pageNum-1}
})
}
} else if (isAtBottom(event) && prevScrollTopRef.current < event.currentTarget.scrollTop) {
if (rows?.length > pageNum* pageSize) {
event.currentTarget.scrollTop = 0;
setPagination(prev => {
return {...prev, pageNum: prev.pageNum+1}
})
}
}
prevScrollTopRef.current = event.currentTarget.scrollTop;
}, [loadMoreRows, rows, pageNum])
return (
<div className='virtual-table'>
<DataGrid
ref={gridRef}
{...rest}
columns={cols}
rows={_rows}
rowKeyGetter={rowKeyGetter}
components={{
sortIcon: SortIcon,
checkboxFormatter: CheckboxFormatter,
rowRenderer: (props) => {
return (
<GridRow
className={rowClassName(props.row)}
id={`row-${props.row.id}`}
onContextMenu={(e: React.MouseEvent) => {
if (props.row.__type__ !== RowType.Detail) {
onContextMenu && onContextMenu(e, props.row);
}
}}
{...props}
/>
)
},
noRowsFallback: <div style={{ textAlign: 'center', gridColumn: '1/-1' }}>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
</div>,
}}
onRowsChange={onRowsChange}
headerRowHeight={38}
rowHeight={(args) => (args.type === 'ROW' && args.row.__type__ === RowType.Detail ? (expandable?.expandedRowHeight||100) : rowHeight)}
// 排序
sortColumns={sortColumns}
onSortColumnsChange={setSortColumns}
selectedRows={new Set(selectedRows||[])}
onSelectedRowsChange={(values: Set<any>) => {
onSelectedRowsChange && onSelectedRowsChange(Array.from(values));
}}
// 滚动
onScroll={handleScroll}
/>
</div>
);
}
export default FC
function GetComparator(sortColumn: string): (a: any, b: any) => number {
return (a, b) => {
const aVal = a[sortColumn], bVal = b[sortColumn]
if (aVal !== undefined && bVal !== undefined)
return aVal.localeCompare(bVal);
return 0
};
}
function isAtTop({ currentTarget }: React.UIEvent<HTMLDivElement>): boolean {
return currentTarget.scrollTop === 0;
}
function isAtBottom({ currentTarget }: React.UIEvent<HTMLDivElement>): boolean {
return currentTarget.scrollTop + 10 >= currentTarget.scrollHeight - currentTarget.clientHeight;
}
function SortIcon({ sortDirection }: SortIconProps) {
const asc = sortDirection === 'ASC'
const desc = sortDirection === 'DESC'
return <div className="yy-table-column-sorter yy-table-column-sorter-full">
<span className="yy-table-column-sorter-inner">
{upNode(asc)}
{downNode(desc)}
</span>
</div>
}
\ No newline at end of file
import CaretDownOutlined from '@ant-design/icons/CaretDownOutlined';
import CaretUpOutlined from '@ant-design/icons/CaretUpOutlined';
import classNames from 'classnames'
const prefixCls = 'yy'
export const upNode = (active:boolean) => (
<CaretUpOutlined
className={classNames(`${prefixCls}-table-column-sorter-up anticon-caret-up anticon`, {
active,
})}
/>
);
export const downNode = (active:boolean) => (
<CaretDownOutlined
className={classNames(`${prefixCls}-table-column-sorter-down anticon-caret-down anticon`, {
active,
})}
/>
);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment