React+TS前台项目实战(十五)-- 全局常用组件Table封装

文章目录

  • 前言
  • Table组件
    • 1. 功能分析
    • 2. 代码+详细注释
    • 3. 使用方式
    • 4. 效果展示
  • 总结


前言

在这篇文章中,我们将对本系列项目中常用的表格组件Table进行自定义封装,以提高性能并适应项目需求。后期也可进行修改和扩展,以满足项目的需求。


Table组件

1. 功能分析

(1)定义了两种表格项组件:TableTitleItem 和 TableContentItem
(2)TableTitleItem 用于显示表格的标题,接受 width 和 title 作为属性,将标题显示在指定宽度的行内
(3)TableContentItem 用于显示表格的内容,接受 width、content 和可选的 to 属性。如果有 to 属性,则使用全局封装的Link组件显示带有链接的内容,否则直接显示内容。
(4)使用 memo 优化组件,以提高性能并避免不必要的重新渲染
(5)可自行加下排序等功能,后续本项目有需要再加上

2. 代码+详细注释

// @/components/Table/index.tsx
import { memo, ReactNode, FC } from "react";
import { TableTitleRowItem, TableContentRowItem, HighlightLink } from "./styled";

// 表格标题项组件
interface TableTitleItemProps {
  width: string; // 宽度
  title: string; // 标题
}

const TableTitleItem: FC<TableTitleItemProps> = ({ width, title }) => (
  <TableTitleRowItem width={width}>
    <div>{title}</div> {/* 显示标题 */}
  </TableTitleRowItem>
);

// 表格内容项组件
interface TableContentItemProps {
  width: string; // 宽度
  content: string | ReactNode; // 内容
  to?: string; // 链接地址
}

const TableContentItem: FC<TableContentItemProps> = ({ width, content, to }) => {
  return (
    <TableContentRowItem width={width}>
      {/* 如果有链接地址,则显示高亮链接,否则显示内容 */}
      {to ? <HighlightLink to={to}>{content}</HighlightLink> : content}
    </TableContentRowItem>
  );
};

// 表格标题项组件添加memo性能优化
export const MemoizedTableTitleItem = memo(TableTitleItem);
// 表格内容项组件添加memo性能优化
export const MemoizedTableContentItem = memo(TableContentItem);

--------------------------------------------------------------------------------------------------------------

// @/components/Table/styled.tsx
import styled, { CSSObject } from "styled-components";
import Link from "../Link";
import variables from "@/styles/variables.module.scss";
interface ITableTitleRowItemProps {
  width: string;
}
interface ITableContentRowItemProps {
  width: string;
}
export const TableTitleRow = styled.div`
  background: white;
  display: flex;
  min-height: 65px;
  border-radius: 6px 6px 0 0;
  padding: 0 20px;
  margin-bottom: 4px;

  @media (max-width: ${variables.mobileBreakPoint}) {
    flex-flow: row wrap;
    min-height: auto;
    padding: 5px 20px;
  }
`;

export const TableTitleRowItem = styled.div<ITableTitleRowItemProps>`
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  width: ${({ width }) => width};
  min-height: 65px;

  div,
  button {
    border: none;
    outline: none;
    background-color: transparent;
    color: #333;
    font-size: 18px;
    font-weight: 450;
    text-align: center;

    @media (max-width: ${variables.largeBreakPoint}) {
      font-size: 16px;
    }
  }

  @media (max-width: ${variables.mobileBreakPoint}) {
    margin: 10px 40px 10px 0;
    min-height: auto;
  }
`;

export const TableContentRow = styled.div`
  position: relative;
  display: flex;
  min-height: 60px;
  background-color: white;
  padding: 20px;

  ::after {
    content: "";
    position: absolute;
    display: block;
    width: auto;
    height: 1px;
    left: 20px;
    right: 20px;
    bottom: 1px;
    background: #d8d8d8;
    transform: ${(): CSSObject => ({
      scaleY: `${Math.ceil((1.0 / window.devicePixelRatio) * 10.0) / 10.0}`,
    })};
  }

  :hover {
    background: #f8f9fa;
  }
`;

export const TableContentRowItem = styled.div<ITableContentRowItemProps>`
  width: ${({ width }) => width};
  color: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  text-overflow: ellipsis;
  font-size: 16px;

  a {
    color: ${({ theme }) => theme.primary};
    &:hover {
      color: ${({ theme }) => theme.primary};
    }
  }
`;

export const HighlightLink = styled(Link)`
  color: ${({ theme }) => theme.primary};
  text-decoration: none;
`;

3. 使用方式

注:这个组件我们模拟一些假数据来演示,可直接复制代码看效果

// @/pages/Nav1/styled.tsx
// 引入组件
import { MemoizedTableContentItem } from "@/components/Table";
// 使用
import { FC, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import Content from "@/components/Content";
import { TableTitleRow, TableContentRow, TableTitleRowItem } from "@/components/Table/styled";
import { BlockListPanel, ContentTable } from "./styled";
import { useMediaQuery } from "@/hooks";
import { localeNumberString } from "@/utils/number";
interface TableTitleData {
  title: string; // 表头名称
  width: string; // 表头宽度
}
interface Block {
  number: number; // 块高
  row1Content?: string; // 第一行内容
  row2Content?: string; // 第二行内容
  row3Content?: string; // 第三行内容
  row4Content?: string; // 第四行内容
  width: string; // 表行宽度
  to?: string; // 链接
}
interface TableContentData {
  width: string; // 表格列宽度
  to?: string; // 链接
  content: string; // 表格内容
}
const Nav2Page: FC = () => {
  const [t] = useTranslation();
  const isMaxW = useMediaQuery(`(max-width: 1200px)`);
  const TableTitles: TableTitleData[] = useMemo(
    () => [
      {
        title: "Row1",
        width: isMaxW ? "16%" : "14%",
      },
      {
        title: "Row2",
        width: isMaxW ? "18%" : "11%",
      },
      {
        title: "Row3",
        width: "20%",
      },
      {
        title: "Row4",
        width: isMaxW ? "33%" : "40%",
      },
      {
        title: "Row5",
        width: isMaxW ? "13%" : "15%",
      },
    ],
    [t, isMaxW]
  );
  // 表格数据
  const [tableData] = useState([
    {
      number: 99663.6333663333,
      row1Content: "row1内容",
      row2Content: "row2内容",
      row3Content: "row3内容",
      row4Content: "row4内容",
      width: "16%",
      to: "/Nav3",
    },
    // ...
  ]);
  // 当前页
  const currentPage = 1;
  // 每页数据
  const transferToRowData = (block: Block, page: number, isMaxW: boolean) => {
    return [
      {
        width: isMaxW ? "16%" : "14%",
        to: `/block/${block.number}`,
        content: localeNumberString(block.number),
      },
      {
        width: isMaxW ? "18%" : "11%",
        content: block.row1Content,
      },
      {
        width: "20%",
        content: block.row2Content,
      },
      {
        width: isMaxW ? "33%" : "40%",
        content: block.row3Content,
      },
      {
        width: isMaxW ? "13%" : "15%",
        content: block.row4Content,
      },
    ] as TableContentData[];
  };
  return (
    <Content>
      <BlockListPanel className="container">
        <ContentTable>
          {/* 表头 */}
          <TableTitleRow>
            {TableTitles.map((data: TableTitleData) => (
              <TableTitleRowItem width={data.width} key={data.title}>
                <div>{data.title}</div>
              </TableTitleRowItem>
            ))}
          </TableTitleRow>
          {/* 表内容 */}
          {tableData.map(
            (item: Block) =>
              item && (
                <TableContentRow key={item.number}>
                  {transferToRowData(item, currentPage, isMaxW).map((data: TableContentData) => {
                    return <MemoizedTableContentItem width={data.width} content={data.content} to={data.to} />;
                  })}
                </TableContentRow>
              )
          )}
        </ContentTable>
      </BlockListPanel>
    </Content>
  );
};
export default Nav2Page;

--------------------------------------------------------------------------------------------------------------

// @/pages/Nav1/styled.tsx
import styled from "styled-components";
import variables from "@/styles/variables.module.scss";
export const BlockListPanel = styled.div`
  @media (min-width: ${variables.mobileBreakPoint}) {
    margin-top: 25px;
    margin-bottom: 40px;
    border-radius: 6px;
    overflow: hidden;
    box-shadow: 0 2px 6px 0 rgb(77 77 77 / 21%);
  }

  @media (max-width: ${variables.mobileBreakPoint}) {
    margin-top: 0;
    padding: 0 20px;

    .blockGreenBackground {
      margin-left: -20px;
      height: 61px;
      width: calc(100% + 40px);
      z-index: 1;
    }
  }
`;

export const ContentTitle = styled.div`
  font-size: 50px;
  color: black;
  margin: 0 auto;
  text-align: center;

  @media (max-width: ${variables.mobileBreakPoint}) {
    font-size: 26px;
  }

  &::after {
    content: "";
    background: ${({ theme }) => theme.primary};
    height: 4px;
    width: 197px;
    display: block;
    margin: 0 auto;

    @media (max-width: ${variables.mobileBreakPoint}) {
      width: 80px;
    }
  }
`;

export const ContentTable = styled.div`
  @media (max-width: ${variables.mobileBreakPoint}) {
    margin-top: -41px;
    z-index: 2;
  }
`;

4. 效果展示

在这里插入图片描述


总结

下一篇讲【全局常用组件Pagination封装】。关注本栏目,将实时更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/734890.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

html--404页面

<!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"> <meta http-equiv"X-UA-Compatible" content"IEedge,chrome1"> <title>404 错误页面不存在&…

【Linux】进程间通信3——线程安全

1.Linux线程互斥 1.1.进程线程间的互斥相关背景概念 临界资源&#xff1a; 多线程执行流共享的资源叫做临界资源。临界区&#xff1a; 每个线程内部&#xff0c;访问临界资源的代码&#xff0c;就叫做临界区。互斥&#xff1a; 任何时刻&#xff0c;互斥保证有且只有一个执行…

一年前 LLM AGI 碎片化思考与回顾系列⑦ · 在SystemⅡ未知之境之中徘徊

阅读提示&#xff1a; 本篇系列内容的是建立于自己过去一年在以LLM为代表的AIGC快速发展浪潮中结合学术界与产业界创新与进展的一些碎片化思考并记录最终沉淀完成&#xff0c;在内容上&#xff0c;与不久前刚刚完稿的那篇10万字文章「融合RL与LLM思想&#xff0c;探寻世界模型以…

乾坤微服务的使用

前言&#xff1a; 在这里整理下用乾坤来开发微服务的一些资料。 使用好处&#xff1a; 使用乾坤可以实现什么效果呢&#xff1f;众所周知&#xff0c;前端的框架五花八门&#xff0c;react/vue/angular等各领风骚&#xff0c;那么如果我们有需要把不同技术栈的项目整合起来&…

UFS Power Mode Change 介绍

一. UFS Power Mode Change简介 1.UFS Power Mode指的是Unipro层的Power State, 也可以称为链路(Link)上的Power Mode, 可以通过配置Unipro Attribute, 然后控制切换Unipro Power State, 当前Power Mode Change有两种触发方式&#xff1a; (1) 通过DME Power Mode Change触发…

Tortoise 删除文件

1、右击需要删除的文件&#xff0c;选择Delete 2、提交

遗传算法求解时间窗车辆路径规划问题(附python代码)

摘要 本研究提出了一种基于遗传算法的车辆路径规划&#xff08;VRP&#xff09;问题求解框架&#xff0c;它能够有效地处理一系列复杂约束&#xff0c;包括软时间窗、硬时间窗、行驶距离限制、车辆最大载重量、多个配送中心的协调、特定的配送顺序&#xff0c;以及多种车型的选…

【Python系列】探索 NumPy 中的 mean 函数:计算平均值的利器

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【AI技术】GPT-4o背后的语音技术猜想

前言&#xff1a; 本篇文章全文credit 给到 台大的李宏毅老师&#xff0c;李宏毅老师在机器学习上风趣幽默、深入浅出的讲解&#xff0c;是全宇宙学AI、讲中文学生的福音&#xff0c;强力推荐李宏毅老师的机器学习课程和深度学习 人工智能导论&#xff1b; 李宏毅老师的个人长…

LabVIEW机器视觉在质量控制中的应用

基于LabVIEW的机器视觉系统在质量控制中应用广泛&#xff0c;通过图像采集、处理和分析&#xff0c;自动检测产品缺陷、测量尺寸和识别标记&#xff0c;提高生产效率和产品质量。下面介绍LabVIEW机器视觉系统在质量控制中的实现方法、应用场景及其优势。 项目背景 在现代制造业…

Redis 入门篇

文章目录 Redis简介关系型数据库:非关系型数据库 Redis应用场景Redis下载和安装Redis 数据类型Redis 常用命令字符串 string 操作命令哈希 hash 操作命令列表 list 操作命令集合 set 操作命令有序集合 sorted set 操作命令通用命令 Jedis 快速入门配置依赖建立连接 / 操作 Jedi…

ShareX,屏幕截图、屏幕录制和文件共享,还提供了丰富的高级功能和自定义选项

ShareX是一个免费开源的Windows应用程序&#xff0c;用于屏幕截图、屏幕录制和文件共享。它不仅支持基本的屏幕截图功能&#xff0c;还提供了丰富的高级功能和自定义选项&#xff0c;使其成为提高工作效率和截图体验的利器。以下是ShareX v16.1.0便携版的主要功能和特色&#x…

NeRF从入门到放弃4: NeuRAD-针对自动驾驶场景的优化

NeuRAD: Neural Rendering for Autonomous Driving 非常值得学习的一篇文章&#xff0c;几乎把自动驾驶场景下所有的优化都加上了&#xff0c;并且也开源了。 和Unisim做了对比&#xff0c;指出Unisim使用lidar指导采样的问题是lidar的垂直FOV有限&#xff0c;高处的东西打不…

Vue: Module “vue“ has no exported member xxx

这个问题让我困扰了好一会儿&#xff0c;我询问了 chatgpt 和各种网站社区&#xff0c;尝试了切换依赖的版本&#xff0c;清除缓存等等&#xff0c;依然没有解决 不过算是有心栽花花不开&#xff0c;无心插柳柳成荫&#xff0c;碰巧解决了&#xff0c;也不知道是不是这个原因&a…

java收徒 java辅导 java试用期辅导 java零基础学习

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末报名辅导&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还有大家…

WinMerge v2 (开源的文件比较/合并工具)

前言 WinMerge 是一款运行于Windows系统下的免费开源的文件比较/合并工具&#xff0c;使用它可以非常方便地比较多个文档内容甚至是文件夹与文件夹之间的文件差异。适合程序员或者经常需要撰写文稿的朋友使用。 一、下载地址 下载链接&#xff1a;http://dygod/source 点击搜…

微信小程序-伪类选择器

一.伪类选择器 结构伪类常见书写方式&#xff1a; 第一类&#xff1a;找第几个孩子 1. :first-child 找第一个孩子2. :last-child 找最后一个孩子3. :nth-child()&#xff0c;正着找数字&#xff1a;写数字几就是找第几个孩子&#xff0c;2n或者even:找偶数2n1或者o…

python数据分析案例-信用卡违约预测分析

一、研究背景和意义 信用卡已经成为现代社会中人们日常生活中不可或缺的支付工具&#xff0c;它不仅为消费者提供了便利&#xff0c;还为商家提供了更广泛的销售渠道。然而&#xff0c;随着信用卡的普及和使用量的增加&#xff0c;信用卡违约问题逐渐成为金融机构面临的重要挑…

Java基础的重点知识-03-方法与数组

文章目录 方法数组 方法 定义方法的格式详解 修饰符 返回值类型 方法名(参数列表){//代码省略...return 结果; }修饰符&#xff1a; public static 固定写法返回值类型&#xff1a; 表示方法运行的结果的数据类型&#xff0c;方法执行后将结果返回到调用者参数列表&#xff1…

Pytho字符串的定义与操作

一、字符串的定义 Python 字符串是字符的序列&#xff0c;用于存储文本数据。字符串可以包括字母、数字、符号和空格。在 Python 中&#xff0c;字符串是不可变的&#xff0c;这意味着一旦创建了一个字符串&#xff0c;就不能更改其中的字符。但是&#xff0c;你可以创建新的字…