Maison  >  Article  >  interface Web  >  Comment réaliser la fixation de l'en-tête de table en réaction

Comment réaliser la fixation de l'en-tête de table en réaction

藏色散人
藏色散人original
2023-01-09 10:25:11904parcourir

Comment implémenter l'en-tête de table fixe dans React : 1. Implémentez l'en-tête de table fixe via le composant Table d'Ant Design ; 2. Utilisez « rc-table » pour implémenter l'en-tête de table fixe sur le terminal mobile ; événement onscroll de div propriété scrollLeft de div.

Comment réaliser la fixation de l'en-tête de table en réaction

L'environnement d'exploitation de ce tutoriel : système Windows 10, React version 18.0.0, ordinateur Dell G3.

Comment réparer l'en-tête en réaction ?

En-tête fixe de la table React/colonnes verrouillées

Le composant Table d'Ant Design est très facile à utiliser. Il a les fonctions d'en-tête fixe et de colonnes verrouillées, mais Ant Design Mobile n'a pas le composant Table. Pour implémenter les fonctions d'en-têtes de tableau fixes et de colonnes verrouillées sur le terminal mobile, vous devriez pouvoir utiliser rc-table, ou bien sûr vous pouvez en écrire un vous-même.

En analysant le tableau d'AntD, nous pouvons voir que le tableau avec un en-tête fixe est composé de deux balises f5d188ed2c074f8b944552db028f98a1, qui sont respectivement imbriquées dans des divs. Celle du haut est l'en-tête du tableau, qui ne contient que ae20bdd317918ca68efdc799512a9b39 ;. Vous trouverez ci-dessous le contenu du tableau, qui contient uniquement 92cee25da80fac49f6fb6eec5fd2c22a. Vous devez écouter l'événement onscroll du div ci-dessous et modifier l'attribut scrollLeft du div ci-dessus, de sorte que lorsque le tableau défile horizontalement, l'en-tête du tableau défile également de manière synchrone. Les colonnes fixes sont obtenues en définissant la position des propriétés CSS de th et td sur sticky et en définissant left ou right sur 0. En même temps, définissez z-index pour que la colonne verrouillée soit toujours affichée en haut.

Une fois que vous aurez compris le principe, il sera plus facile d'écrire du code.

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<any> = (props: TableProps) => {
  const style: React.CSSProperties = props.style || {};
  const maxHeight: string = props.width ? (props.height + &#39;px&#39;) : &#39;unset&#39;;
  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: &#39;key&#39;
    });
  }
  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(&#39;st-table-header&#39;);
    if (tableHeaders.length > 0) {
      tableHeaders[0].scrollLeft = scrollLeft;
    }
  };
  return (
    <div
      className={classNames(&#39;st-table-container&#39;, props.className)}
      style={style}
    >
      <div className="st-table-header">
        <table>
          <colgroup>
            {
              renderCols(columns)
            }
          </colgroup>
          <thead className="st-table-thead">
            <tr>
              {
                columns.map((column: ColumnType, index: number) => {
                  const align: any = column.align || undefined;
                  const title: React.ReactNode = column.title || &#39;&#39;;
                  const fixed: string = leftFixedColumns.includes(index) ? &#39;left&#39; : (rightFixedColumns.includes(index) ? &#39;right&#39; : &#39;&#39;);
                  const fixedClassName: string = fixed ? (&#39;st-table-cell-fix-&#39; + fixed) : &#39;&#39;;
                  return (
                    <th
                      key={index}
                      className={classNames(&#39;st-table-cell&#39;, fixedClassName, column.className)}
                      style={{textAlign: align}}
                    >
                      {title}
                    </th>
                  );
                })
              }
            </tr>
          </thead>
        </table>
      </div>
      <div
        ref={tableBody}
        className="st-table-body"
        style={{maxHeight: maxHeight}}
        onScroll={(e: any) => handleScroll(e.currentTarget)}
      >
        <table style={{width: maxWidth, minWidth: &#39;100%&#39;}}>
          <colgroup>
              {
                renderCols(columns)
              }
            </colgroup>
            <tbody className="st-table-tbody">
              {
                dataSource.map((record: any, index: number) => (
                  <tr key={index} className="st-table-row">
                    {
                      renderCells(columns, leftFixedColumns, rightFixedColumns, record, index)
                    }
                  </tr>
                ))
              }
            </tbody>
        </table>
      </div>
    </div>
  );
};
function classNames(...names: (string | undefined)[]) {
  const currentNames: string[] = [];
  names.forEach((name: (string | undefined)) => {
    if (name) currentNames.push(name);
  });
  return currentNames.join(&#39; &#39;);
}
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 (
      <col
        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) ? &#39;left&#39; : (rightFixedColumns.includes(index) ? &#39;right&#39; : &#39;&#39;);
    const className: string = classNames(&#39;st-table-cell&#39;, column.className, fixed ? (&#39;st-table-cell-fix-&#39; + fixed) : &#39;&#39;);
    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) ? &#39;&#39; : String(rawValue);
    }
    return (
      <td
        key={index}
        className={className}
        style={{textAlign: align}}
      >
        {value}
      </td>
    );
  });
}
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;
      }
    }
  }
}

Ensuite, vous pouvez l'utiliser comme ceci :

views/Test/index.tsx
import React, { FunctionComponent } from &#39;react&#39;;
import Page from &#39;../../components/Page&#39;;
import ScrollableTable from &#39;../../components/ScrollableTable&#39;;
import StoreProvider from &#39;../../stores/products/context&#39;;
import &#39;./index.less&#39;;
const Test: FunctionComponent<any> = (props: any) => {
  let records: any[] = [{
    id: 1,
    productName: &#39;淡泰&#39;,
    amount1: 198,
    amount2: 200,
    amount3: 205.5,
    currency: &#39;人民币&#39;,
    ca: &#39;Amy&#39;
  }, {
    productName: &#39;方润&#39;,
    amount1: 105.5,
    amount2: 100,
    amount3: 108,
    currency: &#39;港元&#39;,
    ca: &#39;Baby&#39;
  }, {
    productName: &#39;医疗基金-1&#39;,
    amount1: 153,
    amount2: 150,
    amount3: 155,
    currency: &#39;人民币&#39;,
    ca: &#39;Emily&#39;
  }, {
    productName: &#39;医疗基金-2&#39;,
    amount1: 302,
    amount2: 300,
    amount3: 290,
    currency: &#39;美元&#39;,
    ca: &#39;Baby&#39;
  }, {
    productName: &#39;医疗基金-3&#39;,
    amount1: 108.8,
    amount2: 100,
    amount3: 130,
    currency: &#39;人民币&#39;,
    ca: &#39;Amy&#39;
  }, {
    productName: &#39;医疗基金-4&#39;,
    amount1: 205,
    amount2: 200,
    amount3: 208,
    currency: &#39;美元&#39;,
    ca: &#39;吴丹&#39;
  }, {
    productName: &#39;医疗基金-5&#39;,
    amount1: 315.5,
    amount2: 300,
    amount3: 280,
    currency: &#39;人民币&#39;,
    ca: &#39;Baby&#39;
  }, {
    productName: &#39;医疗基金-6&#39;,
    amount1: 109,
    amount2: 95,
    amount3: 106,
    currency: &#39;人民币&#39;,
    ca: &#39;Emily&#39;
  }, {
    productName: &#39;恒大私募债&#39;,
    amount1: 213,
    amount2: 200,
    amount3: 208,
    currency: &#39;港元&#39;,
    ca: &#39;吴丹&#39;
  }];
  const totalRecord: any = {
    productName: &#39;合计&#39;,
    amount1: {},
    amount2: {},
    amount3: {}
  };
  records.forEach((record: any) => {
    const currency: string = record.currency;
    [&#39;amount1&#39;, &#39;amount2&#39;, &#39;amount3&#39;].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: &#39;productName&#39;,
    title: &#39;产品名称&#39;,
    width: 90,
    fixed: true
  }, {
    dataKey: &#39;amount1&#39;,
    title: <React.Fragment>上周缴款金额<br/>(万)</React.Fragment>,
    width: 140,
    align: &#39;center&#39;,
    className: &#39;amount&#39;,
    render: calculateTotal
  }, {
    dataKey: &#39;amount2&#39;,
    title: <React.Fragment>上周预约金额<br/>(万)</React.Fragment>,
    width: 140,
    align: &#39;center&#39;,
    className: &#39;amount&#39;,
    render: calculateTotal
  }, {
    dataKey: &#39;amount3&#39;,
    title: <React.Fragment>待本周跟进金额<br/>(万)</React.Fragment>,
    width: 140,
    align: &#39;center&#39;,
    className: &#39;amount&#39;,
    render: calculateTotal
  }, {
    dataKey: &#39;currency&#39;,
    title: &#39;币种&#39;,
    width: 80
  }, {
    dataKey: &#39;ca&#39;,
    title: &#39;CA&#39;,
    width: 80
  }];
  return (
    <StoreProvider>
      <Page
        {...props}
        title="销售统计"
        className="test"
      >
        <div style={{padding: 15}}>
          <ScrollableTable
            width={window.innerWidth - 30}
            height={196}
            columns={columns}
            dataSource={records}
          />
        </div>
      </Page>
    </StoreProvider>
  );
};
function calculateTotal(value: any) {
  if (value instanceof Object) {
    const keys: any[] = Object.keys(value);
    return (
      <React.Fragment>
        {
          keys.map((key: string, index: number) => (
            <span key={index}>
              {`${value[key].toFixed(2)}万${key}`}
            </span>
          ))
        }
      </React.Fragment>
    )
  }
  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;
      }
    }
  }
}

Apprentissage recommandé : "tutoriel vidéo React"

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn