React antd Table 实现单元格点击 表头斜线分组等功能

先上成品示意图:

 几个关键步骤:

一 定义数据结构:

export const data = [
  {
    key: 'row1',
    beforeUpdateLevel: '等级1',
    level_1: '保',
    level_2: '升',
    level_3: '升',
    level_4: '未设置',
    level: 1, // 表示当前行变更前为等级1
  },
  {
    key: 'row2',
    beforeUpdateLevel: '等级2',
    level_1: '降',
    level_2: '保',
    level_3: '升',
    level_4: '升',
    level: 2,
  },
  {
    key: 'row3',
    beforeUpdateLevel: '等级3',
    level_1: '降',
    level_2: '未设置',
    level_3: '未设置',
    level_4: '升',
    level: 3,
  },
  {
    key: 'row4',
    beforeUpdateLevel: '等级4',
    level_1: '降',
    level_2: '未设置',
    level_3: '未设置',
    level_4: '未设置',
    level: 4,
  },
];

二 实现表头分组:

 利用column--->title属性 实现表头对角线分组" data-link-title="antd Table-->column--->title属性 实现表头对角线分组">antd Table-->column--->title属性 实现表头对角线分组


  const columns = [
    // 利用title可以返回ReactNode属性  和 css 实现分组
    {
      title: (
        <div className={styler.headerCell}>
          <div className={styler.afer}>变更后等级</div>
          <div className={styler.before}>变更前等级</div>
        </div>
      ),
      dataIndex: 'beforeUpdateLevel',
      key: 'beforeUpdateLevel',
      // 固定分组这一栏的宽度
      width: 220,
      align: 'center',
    },
    {
      title: '等级1',
      dataIndex: 'level_1',
      align: 'center',
      key: 'level_1',
    },
    {
      title: '等级2',
      dataIndex: 'level_2',
      align: 'center',
      key: 'level_2',
    },
    {
      title: '等级3',
      dataIndex: 'level_3',
      align: 'center',
      key: 'level_3',
    },
    {
      title: '等级4',
      dataIndex: 'level_4',
      align: 'center',
      key: 'level_4',
    },
  ];

 css 实现画对角线:


  .headerCell {
    // 画三角形
    border-top: 43px rgb(250 250 250) solid;
    /*上边框宽度等于表格第一行行高*/
    width: 0px;
    /*让容器宽度为0*/
    height: 0px;
    /*让容器高度为0*/
    border-left: 237px #f8fbff solid;
    /*左边框宽度等于表格第一行第一格宽度*/
    position: relative;
    .afer {
      position: absolute;
      top: -36px;
      left: -70px;
      width: 60px;
      color: #666666;
    }
    .before {
      position: absolute;
      top: -24px;
      left: -226px;
      width: 60px;
      color: #666666;
    }
    // 伪元素画分割线
    &::after {
      content: '';
      position: absolute;
      width: 1px;
      height: 237px;
      top: -140px;
      left: -120px;
      background-color: rgb(239 239 239);
      display: block;
      transform: rotate(-80deg);
    }
  }

 注意:

固定分组表头的宽度,否则响应式可能会引起表头斜线错位等问题

 三:添加单元格点击事件  设置选中状态

利用onCell属性为单元格添加点击事件

 // 设置第一个单元格为默认选中
 const [active, setActive] = useState('row1_level_1');

 const newColums = columns.map((col, index) => {
    return {
      ...col,
      onCell: (record) => ({
        record,
        dataIndex: col.dataIndex,
        title: col.title,
        onClick: () => cellHandleClick(record, col),
      }),
    };
  });


const cellHandleClick = (record, col) => {
    const { key } = record;
    const { dataIndex } = col;
    // 利用record和col的组合key
    setActive(`${key}_${dataIndex}`);
};

 四:完整代码

import React, { useState, useEffect } from 'react';
import { Popover, Table, Tag } from 'antd';
import { data } from './data';
import classNames from 'classnames';
import DownPopover from './DownPopover';
import StayPopover from './StayPopover';
import UpPopover from './UpPopover';
import styler from './index.less';
import { history } from 'umi';

export default function ViewFlow({ onCellClick }) {
  const [active, setActive] = useState('row1_level_1');
  const SettingCell = ({
    title,
    dataIndex,
    children,
    record,
    ...restProps
  }) => {
    let childNode = children;
    const [show, setShow] = useState(false);
    useEffect(() => {}, [show]);

    const handleOver = () => {
      setShow(true);
    };

    const handleOut = () => {
      setShow(false);
    };

    const SettingBtn = (
      <div
        className={styler.settingBtn}
        onClick={() => handleGoSetting(record, dataIndex)}
      >
        去设置
      </div>
    );

    const renderText = (record, dataIndex) => {
      const { level } = record;
      const [_, currentLevel] = dataIndex.split('_');
      const text = record?.[dataIndex];
      switch (text) {
        case '未设置':
          return show ? SettingBtn : <div className={styler.base}>{text}</div>;
        case '保':
          return (
            <Item
              text={text}
              level={level}
              currentLevel={currentLevel}
              color="#3880FF"
              background="rgba(56, 128, 255, 0.05)"
            />
          );
        case '降':
          return (
            <Item
              text={text}
              level={level}
              currentLevel={currentLevel}
              color="#FF3D00"
              background="rgba(255, 61, 0, 0.05)"
            />
          );
        case '升':
          return (
            <Item
              text={text}
              level={level}
              currentLevel={currentLevel}
              color="#24B488"
              background="rgba(36, 180, 136, 0.05)"
            />
          );
        default:
          return text;
      }
    };

    childNode = renderText(record, dataIndex);
    const key = `${record?.key}_${dataIndex}`;
    return (
      <td
        onMouseOut={handleOut}
        onMouseOver={handleOver}
        {...restProps}
        className={classNames(
          { active: key === active },
          { firstColumnClass: dataIndex === 'beforeUpdateLevel' },
        )}
      >
        {childNode}
      </td>
    );
  };
  const ItemMap = {
    升: UpPopover,
    降: DownPopover,
    保: StayPopover,
  };
  const Item = ({ color, background, text, level, currentLevel }) => {
    const Content = ItemMap[text];
    return (
      <Popover
        content={<Content level={level} currentLevel={currentLevel} />}
        placement="bottomRight"
      >
        <div className={styler.text}>
          <div style={{ color, background }}>{text}</div>
        </div>
      </Popover>
    );
  };


  const handleGoSetting = (record, dataIndex) => {
    const { level } = record;
    const [, levelNum] = dataIndex.split('_');
  
  };

  const cellHandleClick = (record, col) => {
    const { key } = record;
    const { dataIndex } = col;
    setActive(`${key}_${dataIndex}`);
  };

  const columns = [
    {
      title: (
        <div className={styler.headerCell}>
          <div className={styler.afer}>变更后等级</div>
          <div className={styler.before}>变更前等级</div>
        </div>
      ),
      dataIndex: 'beforeUpdateLevel',
      key: 'beforeUpdateLevel',
      width: 220,
      align: 'center',
    },
    {
      title: '等级1',
      dataIndex: 'level_1',
      align: 'center',
      key: 'level_1',
    },
    {
      title: '等级2',
      dataIndex: 'level_2',
      align: 'center',
      key: 'level_2',
    },
    {
      title: '等级3',
      dataIndex: 'level_3',
      align: 'center',
      key: 'level_3',
    },
    {
      title: '等级4',
      dataIndex: 'level_4',
      align: 'center',
      key: 'level_4',
    },
  ];
  const newColums = columns.map((col, index) => {
    return {
      ...col,
      onCell: (record) => ({
        record,
        dataIndex: col.dataIndex,
        title: col.title,
        onClick: () => cellHandleClick(record, col),
      }),
    };
  });

  const components = {
    body: {
      cell: SettingCell,
    },
  };

  return (
    <div className={styler.content}>
      <Table
        components={components}
        columns={newColums}
        bordered
        dataSource={data}
        pagination={false}
      />
    </div>
  );
}

data.js

export const data = [
  {
    key: 'row1',
    beforeUpdateLevel: '等级1',
    level_1: '保',
    level_2: '升',
    level_3: '升',
    level_4: '未设置',
    level: 1, // 表示当前行变更前为等级1
  },
  {
    key: 'row2',
    beforeUpdateLevel: '等级2',
    level_1: '降',
    level_2: '保',
    level_3: '升',
    level_4: '升',
    level: 2,
  },
  {
    key: 'row3',
    beforeUpdateLevel: '等级3',
    level_1: '降',
    level_2: '未设置',
    level_3: '未设置',
    level_4: '升',
    level: 3,
  },
  {
    key: 'row4',
    beforeUpdateLevel: '等级4',
    level_1: '降',
    level_2: '未设置',
    level_3: '未设置',
    level_4: '未设置',
    level: 4,
  },
];

index.less

.content {
  .base {
    padding: 5px 0px;
    color: #c8c8c8;
    width: 60px;
    display: inline-block;
  }
  .settingBtn {
    box-sizing: border-box;
    display: inline-block;
    background: rgba(56, 128, 255, 0.1);
    border: 1px solid rgba(56, 128, 255, 0.5);
    padding: 4px;
    width: 60px;
    border-radius: 2px;
    color: #3880ff;
  }
  .text {
    box-sizing: border-box;
    width: 60px;
    padding: 4px 0;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    div {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      background: rgba(56, 128, 255, 0.05);
    }
  }
  .headerCell {
    // 画三角形
    border-top: 43px rgb(250 250 250) solid;
    /*上边框宽度等于表格第一行行高*/
    width: 0px;
    /*让容器宽度为0*/
    height: 0px;
    /*让容器高度为0*/
    border-left: 237px #f8fbff solid;
    /*左边框宽度等于表格第一行第一格宽度*/
    position: relative;
    .afer {
      position: absolute;
      top: -36px;
      left: -70px;
      width: 60px;
      color: #666666;
    }
    .before {
      position: absolute;
      top: -24px;
      left: -226px;
      width: 60px;
      color: #666666;
    }
    // 伪元素画分割线
    &::after {
      content: '';
      position: absolute;
      width: 1px;
      height: 237px;
      top: -140px;
      left: -120px;
      background-color: rgb(239 239 239);
      display: block;
      transform: rotate(-80deg);
    }
  }

  :global {
    .firstColumnClass {
      background: #f8fbff;
    }
    .active {
      background: linear-gradient(
        0deg,
        rgba(56, 128, 255, 0.05),
        rgba(56, 128, 255, 0.05)
      );
      // border: 1px solid #3880ff;
      // border-right: 1px solid #3880ff !important ;
      // box-sizing: border-box;
      box-shadow: 0px 0px 2px #3880ff inset;
    }
    .ant-table-thead {
      height: 30px;
    }
    .ant-table-tbody > tr > td {
      padding: 8px 0px;
    }
    .ant-table.ant-table-bordered
      > .ant-table-container
      > .ant-table-content
      > table
      > thead
      > tr
      > th {
      padding: 0;
    }
    // .ant-table-tbody > tr > td :first-child {
    //   background: #f8fbff;
    //   &:hover {
    //     background: #f8fbff;
    //   }
    // }background: #f8fbff;
    // .ant-table-tbody > tr > td:nth-child(2) {
    //   background: #bae7ff;
    // }

    .ant-table-tbody > tr > td:hover {
      cursor: pointer;
      background: linear-gradient(
        0deg,
        rgba(56, 128, 255, 0.05),
        rgba(56, 128, 255, 0.05)
      );
    }
  }
}

DownPopver.jsx

import React from 'react';
import { Popover, Space, Tag, Typography } from 'antd';
export default function DownPopover({ level, currentLevel }) {
  return (
    <Space size={10} direction="vertical">
      <Tag color="#FF3D00">降级</Tag>
      <>
        <Typography.Text type="secondary">
          变更前等级:<Typography.Text>等级{`${level}`}</Typography.Text>
        </Typography.Text>
        <Typography.Text type="secondary">
          变更后等级:
          <Typography.Text>等级{`${currentLevel}`}</Typography.Text>
        </Typography.Text>
      </>
    </Space>
  );
}

 StayPopover.jsx

import React from 'react';
import { Popover, Space, Tag, Typography } from 'antd';
export default function StayPopover({ level, currentLevel }) {
  return (
    <Space size={10} direction="vertical">
      <Tag color="#599CFF">保级</Tag>
      <>
        <Typography.Text type="secondary">
          保级等级:<Typography.Text>等级{`${currentLevel}`}</Typography.Text>
        </Typography.Text>
      </>
    </Space>
  );
}

 UpPopover.jsx

import React from 'react';
import { Popover, Space, Tag, Typography } from 'antd';
export default function UpPopover({ level, currentLevel }) {
  return (
    <Space size={10} direction="vertical">
      <Tag color="#24B488">升级</Tag>
      <>
        <Typography.Text type="secondary">
          变更前等级:<Typography.Text>等级{`${level}`}</Typography.Text>
        </Typography.Text>
        <Typography.Text type="secondary">
          变更后等级:
          <Typography.Text>等级{`${currentLevel}`}</Typography.Text>
        </Typography.Text>
        <Typography.Text type="secondary">
          状态:<Typography.Text>启用</Typography.Text>
        </Typography.Text>
      </>
    </Space>
  );
}

 

React Ant Design 中,使用 `rowClassName` 属性可以为表格的每一行添加样式。具体实现方法如下: 1. 首先,在表格组件中设置 `rowClassName` 属性,值为一个回调函数。 2. 回调函数接收一个参数,即当前行的数据对象,可以根据这个数据对象来判断应该为当前行添加哪些样式。 3. 在回调函数中返回一个字符串,字符串即为添加给当前行的样式类名。 下面是一个简单的例子: ```jsx import React from 'react'; import { Table } from 'antd'; const dataSource = [ { key: '1', name: 'John Brown', age: 32, address: 'New York No. 1 Lake Park', }, { key: '2', name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park', }, { key: '3', name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park', }, ]; const rowClassName = (record, index) => { if (index % 2 === 0) { return 'even-row'; } else { return 'odd-row'; } }; const columns = [ { title: 'Name', dataIndex: 'name', key: 'name', }, { title: 'Age', dataIndex: 'age', key: 'age', }, { title: 'Address', dataIndex: 'address', key: 'address', }, ]; const App = () => { return <Table dataSource={dataSource} columns={columns} rowClassName={rowClassName} />; }; export default App; ``` 在上面的例子中,我们定义了一个回调函数 `rowClassName`,该函数接收两个参数:`record` 表示当前行的数据对象,`index` 表示当前行的索引。在该函数中,我们判断当前行的索引是否为偶数,如果是偶数,就返回样式类名 `even-row`,否则返回样式类名 `odd-row`。最后将该函数作为 `rowClassName` 属性传递给 `Table` 组件,即可实现为表格的每一行添加背景色的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superTiger_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值