antd 设置表头属性_React表格固定表头/锁定列

Ant Design的Table组件挺好用,固定表头及锁定列的功能不在话下,但Ant Design Mobile没有Table组件。移动端要实现表格固定表头及锁定列的功能应该可以使用rc-table,当然也可以自己写一个。

通过分析AntD的Table,可以看出固定表头的表格是由上下两个

原理整明白了,写代码就比较容易了。

components/ScrollableTable/interface.tsx

import * as React from 'react';

export declare type AlignType = 'left' | 'center' | 'right';

export interface ColumnType {

align?: AlignType;

className?: string;

dataKey?: string;

fixed?: boolean;

title?: React.ReactNode;

width?: number;

render?: (value: any, record: any, index: number) => React.ReactNode;

}

export interface TableProps {

className?: string;

style?: React.CSSProperties;

columns?: ColumnType[];

dataSource?: any[];

width?: number;

height?: number;

}

components/ScrollableTable/index.tsx

import React, { FunctionComponent, useRef } from 'react';

import { TableProps, ColumnType } from './interface';

import './index.less';

const ScrollableTable: FunctionComponent = (props: TableProps) => {

const style: React.CSSProperties = props.style || {};

const maxHeight: string = props.width ? (props.height + 'px') : 'unset';

const columns: ColumnType[] = props.columns || [];

const dataSource: any[] = props.dataSource || [];

let maxWidth: number = 0;

if (props.width) style.width = props.width;

if (columns.length === 0) {

columns.push({

dataKey: 'key'

});

}

columns.forEach((column: ColumnType) => {

const width: number = column.width || 50;

maxWidth += width;

});

const fixedColumns: number[][] = getFixedColumns(columns);

const leftFixedColumns: number[] = fixedColumns[0];

const rightFixedColumns: number[] = fixedColumns[1];

const tableBody: any = useRef();

const handleScroll = (target: any) => {

const scrollLeft: number = target.scrollLeft;

const tableHeaders: any = target.parentElement.getElementsByClassName('st-table-header');

if (tableHeaders.length > 0) {

tableHeaders[0].scrollLeft = scrollLeft;

}

};

return (

className={classNames('st-table-container', props.className)}

style={style}

>

{

renderCols(columns)

}

{

columns.map((column: ColumnType, index: number) => {

const align: any = column.align || undefined;

const title: React.ReactNode = column.title || '';

const fixed: string = leftFixedColumns.includes(index) ? 'left' : (rightFixedColumns.includes(index) ? 'right' : '');

const fixedClassName: string = fixed ? ('st-table-cell-fix-' + fixed) : '';

return (

key={index}

className={classNames('st-table-cell', fixedClassName, column.className)}

style={{textAlign: align}}

>

{title}

);

})

}

ref={tableBody}

className="st-table-body"

style={{maxHeight: maxHeight}}

onScroll={(e: any) => handleScroll(e.currentTarget)}

>

{

renderCols(columns)

}

{

dataSource.map((record: any, index: number) => (

{

renderCells(columns, leftFixedColumns, rightFixedColumns, record, index)

}

))

}

);

};

function classNames(...names: (string | undefined)[]) {

const currentNames: string[] = [];

names.forEach((name: (string | undefined)) => {

if (name) currentNames.push(name);

});

return currentNames.join(' ');

}

function getFixedColumns(columns: ColumnType[]) {

const total: number = columns.length;

const leftFixedColumns: number[] = [];

const rightFixedColumns: number[] = [];

if (columns[0].fixed) {

for (let i = 0; i < total; i++) {

if (columns[i].fixed) {

leftFixedColumns.push(i);

} else {

break;

}

}

}

if (columns[total - 1].fixed) {

for (let i = total - 1; i >= 0; i--) {

if (columns[i].fixed) {

if (!leftFixedColumns.includes(i)) rightFixedColumns.push(i);

} else {

break;

}

}

}

return [leftFixedColumns, rightFixedColumns];

}

function renderCols(columns: ColumnType[]) {

return columns.map((column: ColumnType, index: number) => {

const width: number = column.width || 50;

return (

key={index}

style={{width: width, minWidth: width}}

/>

);

});

}

function renderCells(columns: ColumnType[], leftFixedColumns: number[], rightFixedColumns: number[], record: any, index: number) {

return columns.map((column: ColumnType, index: number) => {

const align: any = column.align || undefined;

const fixed: string = leftFixedColumns.includes(index) ? 'left' : (rightFixedColumns.includes(index) ? 'right' : '');

const className: string = classNames('st-table-cell', column.className, fixed ? ('st-table-cell-fix-' + fixed) : '');

const rawValue: any = (column.dataKey && column.dataKey in record) ? record[column.dataKey] : undefined;

let value: any = undefined;

if (column.render) {

value = column.render(rawValue, record, index);

} else {

value = (rawValue === undefined || rawValue === null) ? '' : String(rawValue);

}

return (

key={index}

className={className}

style={{textAlign: align}}

>

{value}

);

});

}

export default ScrollableTable;

components/ScrollableTable/index.less

.st-table-container {

border: 1px solid #f0f0f0;

border-right: 0;

border-bottom: 0;

font-size: 14px;

.st-table-header {

border-right: 1px solid #f0f0f0;

overflow: hidden;

table {

border-collapse: separate;

border-spacing: 0;

table-layout: fixed;

width: 100%;

thead.st-table-thead {

tr {

th.st-table-cell {

background: #fafafa;

border-bottom: 1px solid #f0f0f0;

border-right: 1px solid #f0f0f0;

color: rgba(0, 0, 0, .85);

font-weight: 500;

padding: 8px;

text-align: left;

&:last-child {

border-right: 0;

}

}

}

}

}

}

.st-table-body {

overflow: auto scroll;

border-bottom: 1px solid #f0f0f0;

border-right: 1px solid #f0f0f0;

table {

border-collapse: separate;

border-spacing: 0;

table-layout: fixed;

tbody.st-table-tbody {

tr.st-table-row {

td.st-table-cell {

border-bottom: 1px solid #f0f0f0;

border-right: 1px solid #f0f0f0;

color: rgba(0, 0, 0, .65);

padding: 8px;

text-align: left;

&:last-child {

border-right: 0;

}

}

&:last-child {

td.st-table-cell {

border-bottom: 0;

}

}

}

}

}

}

table {

.st-table-cell {

&.st-table-cell-fix-left {

background: #fff;

position: sticky;

left: 0;

z-index: 2;

}

&.st-table-cell-fix-right {

background: #fff;

position: sticky;

right: 0;

z-index: 2;

}

}

}

}

然后可以这样使用:

views/Test/index.tsx

import React, { FunctionComponent } from 'react';

import Page from '../../components/Page';

import ScrollableTable from '../../components/ScrollableTable';

import StoreProvider from '../../stores/products/context';

import './index.less';

const Test: FunctionComponent = (props: any) => {

let records: any[] = [{

id: 1,

productName: '淡泰',

amount1: 198,

amount2: 200,

amount3: 205.5,

currency: '人民币',

ca: 'Amy'

}, {

productName: '方润',

amount1: 105.5,

amount2: 100,

amount3: 108,

currency: '港元',

ca: 'Baby'

}, {

productName: '医疗基金-1',

amount1: 153,

amount2: 150,

amount3: 155,

currency: '人民币',

ca: 'Emily'

}, {

productName: '医疗基金-2',

amount1: 302,

amount2: 300,

amount3: 290,

currency: '美元',

ca: 'Baby'

}, {

productName: '医疗基金-3',

amount1: 108.8,

amount2: 100,

amount3: 130,

currency: '人民币',

ca: 'Amy'

}, {

productName: '医疗基金-4',

amount1: 205,

amount2: 200,

amount3: 208,

currency: '美元',

ca: '吴丹'

}, {

productName: '医疗基金-5',

amount1: 315.5,

amount2: 300,

amount3: 280,

currency: '人民币',

ca: 'Baby'

}, {

productName: '医疗基金-6',

amount1: 109,

amount2: 95,

amount3: 106,

currency: '人民币',

ca: 'Emily'

}, {

productName: '恒大私募债',

amount1: 213,

amount2: 200,

amount3: 208,

currency: '港元',

ca: '吴丹'

}];

const totalRecord: any = {

productName: '合计',

amount1: {},

amount2: {},

amount3: {}

};

records.forEach((record: any) => {

const currency: string = record.currency;

['amount1', 'amount2', 'amount3'].forEach((key: string) => {

const value: any = totalRecord[key];

if (!(currency in value)) value[currency] = 0;

value[currency] += record[key];

});

});

records.push(totalRecord);

const columns: any[] = [{

dataKey: 'productName',

title: '产品名称',

width: 90,

fixed: true

}, {

dataKey: 'amount1',

title: 上周缴款金额
(万),

width: 140,

align: 'center',

className: 'amount',

render: calculateTotal

}, {

dataKey: 'amount2',

title: 上周预约金额
(万),

width: 140,

align: 'center',

className: 'amount',

render: calculateTotal

}, {

dataKey: 'amount3',

title: 待本周跟进金额
(万),

width: 140,

align: 'center',

className: 'amount',

render: calculateTotal

}, {

dataKey: 'currency',

title: '币种',

width: 80

}, {

dataKey: 'ca',

title: 'CA',

width: 80

}];

return (

{...props}

title="销售统计"

className="test"

>

width={window.innerWidth - 30}

height={196}

columns={columns}

dataSource={records}

/>

);

};

function calculateTotal(value: any) {

if (value instanceof Object) {

const keys: any[] = Object.keys(value);

return (

{

keys.map((key: string, index: number) => (

{`${value[key].toFixed(2)}万${key}`}

))

}

)

}

return value.toFixed(2);

}

export default Test;

views/Test/index.less

.st-table-container {

.st-table-body {

td.st-table-cell.amount {

padding-right: 20px !important;

text-align: right !important;

span {

display: block;

}

}

}

}


版权声明:本文为weixin_39753211原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。