antd table 合并相邻相同单元格数据

news2024/11/25 11:35:57

antd 表格行/列合并

定义及使用

表头只支持列合并,使用 column 里的 colSpan 进行设置。

表格支持行/列合并,使用 onCell 里的单元格属性 colSpan 或者 rowSpan 设置。 设置为 0 时,设置的表格不会渲染(所以在设置的时候前面的行/列合并了后面的行/列时,后面的被合并的行/列渲染时需要设为 0)。

1、表头列合并时,在 table 定义 columns 时指定 colSpan 即可,为了显示正常要在被前面合并列的后面的几个 column 中指定 colSpan: 0
在这里插入图片描述

示例代码:

const columns = [
  {
    title: "Home phone",
    colSpan: 2,
    dataIndex: "tel",
  },
  {
    title: "Phone",
    colSpan: 0,
    dataIndex: "phone",
  },
];

上面 Home Phone 列指定 colSpan:2 合并了两列(当前列和后面的 Phone 列),为了显示正常(表头不会多出一列),在 Phone 列中需要指定 colSpan: 0

2、数据行合并,在 onCell 函数中返回一个包含 rowSpan 参数的对象
在这里插入图片描述

示例代码:

{
  title: 'Home phone',
  colSpan: 2,
  dataIndex: 'tel',
  onCell: (_, index) => {
    if (index === 2) {
      return {
        rowSpan: 2,
      };
    }
    // These two are merged into above cell
    if (index === 3) {
      return {
        rowSpan: 0,
      };
    }
    if (index === 4) {
      return {
        colSpan: 0,
      };
    }
    return {};
  },
}

上面代码表示在 Home phone 这一列,第 3 行跨两行即合并第 3、4 为一个单元格。第 4 行由于是被合并所以设置为 0,第五行由于列被合并所以 colSpan 设为 0

3、数据列合并,在 onCell 函数中返回一个包含 colSpan 参数的对象
在这里插入图片描述

示例代码:

{
  title: 'Name',
  dataIndex: 'name',
  render: (text) => <a>{text}</a>,
  onCell: (_, index) => ({
    colSpan: index < 4 ? 1 : 5,
  }),
}

上面代码代表 Name 这一列,在第 5 行前面不跨列,在不小于第 5 行后跨 5 列(包含自身)

相邻数据行合并

思维衍生,根据这个我们可以做一个相同数据单元格合并的功能,具体为:
1、如果多行相邻的行数据,上下相邻的单元格数据相同,就把上下相邻且相同的单元格合并。
2、可以给上面的单元格合并加一个限制条件,例如前面的第一列 id 相同的情况下,且出现上述 1 的相邻相同数据才合并

图例:
在这里插入图片描述

  • 上述我们可以用以 name 相同作为合并的前提条件,合并后面的 Age 列。
  • 以 Age 相同作为合并的前提条件,合并后面的 Home phone 列。
  • 不设置前置条件合并所有上下相邻的且值相同的列:phone。
  • 以 Home phone 相同作为合并的前提条件,合并后面的 Address列。

原理:
onCell 方法在当前列的每个单元格都会执行一次,首先会判断当前单元格是已经被合并。

  • 如果否,需要合并的列执行的时候记录当前单元格的值,从前往后遍历表格数据(tableData),遇到相同列(上下相邻行),并且相同数据的行,就记录并把合并行的计数+1,直到遇到不同的值就返回需要合并的行总数(如{rowSpan: 2})。
  • 如果是,后面已经被合并的单元格执行 onCell 方法时,会去rowSpanRecord查看自己是否被合并了,合并了就返回不渲染当前单元格(即返回 {rowSpan:0})

示例代码:

/* 
rowSpanRecord 数据结构
rowSpanRecord = {
  name: {
    'John Browne': {
      rowIndexs: [0]
    },
    'Jim Green': {
      rowIndexs: [1,2]
    },
    'Joe Black': {
      rowIndexs: [3]
    },
    'Jim Red': {
      rowIndexs: [4]
    },
    'Jake White': {
      rowIndexs: [5]
    },
  },
  age: {
    32: {
      rowIndexs: [0,1,2]
    },
    42: {
      rowIndexs: [3]
    },
    16: {
      rowIndexs: [4,5]
    },
  },
  ...
}
*/
let rowSpanRecord = {
  age: {},
  tel: {}
};
columns = [
  {
    title: "Age",
    dataIndex: "age",
    // 无前置合并条件的,相同行直接合并
    onCell: (record, rowIndex) => {
      if (rowSpanRecord["age"][record.age]?.rowIndexs.includes(rowIndex)) {
        return { rowSpan: 0 };
      }
      let rowSpan = 0;
      for (let i = rowIndex; i < data.length; i++) {
        if (record.age === data[i].age) {
          rowSpan++;
          if (rowSpanRecord["age"][record.age]) {
            rowSpanRecord["age"][record.age].records.push(data[i]);
            rowSpanRecord["age"][record.age].rowIndexs.push(i);
          } else {
            rowSpanRecord["age"][record.age] = {
              records: [data[i]],
              rowIndexs: [i]
            };
          }
        } else {
          break;
        }
      }
      return { rowSpan };
    }
  },
  {
    title: "Home phone",
    dataIndex: "tel",
    // 以age相同为合并条件
    onCell: (record, rowIndex) => {
      if (rowSpanRecord["tel"][record.tel]?.rowIndexs.includes(rowIndex)) {
        return { rowSpan: 0 };
      }
      let rowSpan = 0;
      for (let i = rowIndex; i < data.length; i++) {
        if (record.age === data[i].age && record.tel === data[i].tel) {
          rowSpan++;
          if (rowSpanRecord["tel"][record.tel]) {
            rowSpanRecord["tel"][record.tel].records.push(data[i]);
            rowSpanRecord["tel"][record.tel].rowIndexs.push(i);
          } else {
            rowSpanRecord["tel"][record.tel] = {
              records: [data[i]],
              rowIndexs: [i]
            };
          }
        } else {
          break;
        }
      }
      return { rowSpan };
    }
  }
]

将 onCell 合并中合并单元格的方法进行抽象封装,核心代码:

/**
 * 合并目标列中相同值的行
 * @param record antd 提供的当前行数据
 * @param rowIndex antd 提供的当前行的序号
 * @param data 当前table的数据列表 tableData
 * @param curProp 当前需要合并行的列的属性值
 * @param sameKey 可选, 当前要合并行的列的前置条件,就是前面必须那个属性值必须相同才判断当前列相同合并
 * @returns {rowSpan: num}, 返回一个包含要合并的行数的对象
 */
const mergeRows = ({
  record = {},
  rowIndex = 0,
  data = [],
  curProp = "",
  sameKey = undefined
} = {}) => {
  /* 前面行已经进行了判断并合并,前面的行合并了后面的行之后,(该判断是后续行的判断)
  后面的行不能再显示了否则table会变形,所以这里需要将后续的列中返回 {rowSpan:0} */
  if (rowSpanRecord[curProp][record[curProp]]?.rowIndexs.includes(rowIndex)) {
    return { rowSpan: 0 };
  }
  let rowSpan = 0;
  for (let i = rowIndex; i < data.length; i++) {
    // 有 sameKey,需要判断前置条件列中的值必须相同
    if (sameKey) {
      if (
        record[sameKey] === data[i][sameKey] &&
        record[curProp] === data[i][curProp]
      ) {
        rowSpan++;
        // 将列中行的值记录
        if (rowSpanRecord[curProp][record[curProp]]) {
          rowSpanRecord[curProp][record[curProp]].rowIndexs.push(i);
        } else {
          rowSpanRecord[curProp][record[curProp]] = {
            rowIndexs: [i]
          };
        }
      } else {
        break;
      }
    } else {
      // 没有前置列,只需要判断当前列相邻列是否相同
      if (record[curProp] === data[i][curProp]) {
        rowSpan++;
        // 将列中行的值记录
        if (rowSpanRecord[curProp][record[curProp]]) {
          rowSpanRecord[curProp][record[curProp]].rowIndexs.push(i);
        } else {
          rowSpanRecord[curProp][record[curProp]] = {
            rowIndexs: [i]
          };
        }
      } else {
        break;
      }
    }
  }
  return { rowSpan };
};

补充

antd4 中使用时发现每次刷新后会有 bug,导致合并行全都不见了,原因:antd 在浏览器刷新的时候或者说在第一次渲染时 onCell 回调函数会调用两次,所以需要对后面每次重新调用进行一个数据重置。

另外在使用的时候 rowSpanRecord 合并行数据记录对象和 mergeRows 方法都可以放到组件外面

// 当前行数据,当前列的值
const recordCurPropValue = record[curProp];
// 判断是否是重复渲染(antd刷新或者第一次渲染会触发两次onCell回调),是 则重置数据
if (
  rowIndex === 0 &&
  rowSpanRecord[curProp][recordCurPropValue]?.rowIndexs?.length > 0
) {
  for (const key of Object.keys(rowSpanRecord)) {
    rowSpanRecord[key] = {};
  }
}

完整代码:

import React from "react";
import "antd/dist/antd.css";
import "./index.css";
import { Table } from "antd";
import type { ColumnsType } from "antd/es/table";

interface DataType {
  key: string;
  name: string;
  age: number;
  tel: string;
  phone: number;
  address: string;
}

const data: DataType[] = [
  {
    key: "1",
    name: "John Browne",
    age: 32,
    tel: "0571-22098909",
    phone: 18889898989,
    address: "New York "
  },
  {
    key: "2",
    name: "Jim Green",
    tel: "0571-22098333",
    phone: 18889898989,
    age: 32,
    address: "London No. "
  },
  {
    key: "20",
    name: "Jim Green",
    tel: "0571-22098333",
    phone: 18889898989,
    age: 32,
    address: "London No. "
  },
  {
    key: "3",
    name: "Joe Black",
    age: 42,
    tel: "0575-22098909",
    phone: 18900010002,
    address: "Sidney No."
  },
  {
    key: "4",
    name: "Jim Red",
    age: 16,
    tel: "0575-220989091",
    phone: 18900010002,
    address: "Sidney No."
  },
  {
    key: "5",
    name: "Jake White",
    age: 16,
    tel: "0575-220989091",
    phone: 18900010002,
    address: "Sidney No."
  }
];

/*  需要进行行合并的列的属性值对象,主要为了记录每次合并时的行, rowIndexs 为记录的相同值的行号
结构: rowSpanRecord: {age: {rowIndexs: [0,1,2,3]}} */
let rowSpanRecord = {
  age: {},
  tel: {},
  phone: {},
  address: {}
};

/**
 * 合并目标列中相同值的行
 * @param record antd 提供的当前行数据
 * @param rowIndex antd 提供的当前行的序号
 * @param data 当前table的数据列表 tableData
 * @param curProp 当前需要合并行的列的属性值
 * @param sameKey 可选, 当前要合并行的列的前置条件,就是前面必须那个属性值必须相同才判断当前列相同合并
 * @returns {rowSpan: num}, 返回一个包含要合并的行数的对象
 */
const mergeRows = ({
  record = {},
  rowIndex = 0,
  data = [],
  curProp = "",
  sameKey = undefined,
} = {}) => {
  // 当前行数据,当前列的值
  const recordCurPropValue = record[curProp];
  // 判断是否是重复渲染(antd刷新或者第一次渲染会触发两次onCell回调)
  if (
    rowIndex === 0 &&
    rowSpanRecord[curProp][recordCurPropValue]?.rowIndexs?.length > 0
  ) {
    for (const key of Object.keys(rowSpanRecord)) {
      rowSpanRecord[key] = {};
    }
  }
  // 记录中,当前要合并的列的prop,下面值的集合
  const rowSpanRecordCurPorp = rowSpanRecord[curProp];

  /**
       * 前面行已经进行了判断并合并,前面的行合并了后面的行之后,(该判断是后续行的判断)
      后面的行不能再显示了否则table会变形,所以这里需要将后续的列中返回 {rowSpan:0}
       */
  const curPropIndexs = rowSpanRecordCurPorp[recordCurPropValue]?.rowIndexs;
  if (curPropIndexs?.includes(rowIndex)) {
    return { rowSpan: 0 };
  }
  let rowSpan = 0;
  for (let i = rowIndex; i < data.length; i++) {
    // 有 sameKey,需要判断前置条件列中的值必须相同
    if (sameKey) {
      if (
        record[sameKey] === data[i][sameKey] &&
        recordCurPropValue === data[i][curProp]
      ) {
        rowSpan += 1;
        // 将列中行的值记录
        if (rowSpanRecordCurPorp[recordCurPropValue]) {
          rowSpanRecordCurPorp[recordCurPropValue].rowIndexs.push(i);
        } else {
          rowSpanRecordCurPorp[recordCurPropValue] = {
            rowIndexs: [i],
          };
        }
      } else {
        break;
      }
    } else {
      // 没有前置列,只需要判断当前列相邻列是否相同
      if (recordCurPropValue === data[i][curProp]) {
        rowSpan += 1;
        // 将列中行的值记录
        if (rowSpanRecordCurPorp[recordCurPropValue]) {
          rowSpanRecordCurPorp[recordCurPropValue].rowIndexs.push(i);
        } else {
          rowSpanRecordCurPorp[recordCurPropValue] = {
            rowIndexs: [i],
          };
        }
      } else {
        break;
      }
    }
  }
  return { rowSpan };
};

const columns: ColumnsType<DataType> = [
  {
    title: "Name",
    dataIndex: "name",
    render: (text) => <a>{text}</a>
  },
  {
    title: "Age",
    dataIndex: "age",
    onCell: (record, rowIndex) => {
      // 这是第一个指明相同的合并的列,前面的列可以没有合并,比如name
      return mergeRows({
        record,
        rowIndex,
        data,
        curProp: "age",
        sameKey: "name"
      });
    }
  },
  {
    title: "Home phone",
    dataIndex: "tel",
    onCell: (record, rowIndex) => {
      // 指明前置条件 age 必须相同,只有当age相同时,当前列有相邻相同行值才会合并
      return mergeRows({
        record,
        rowIndex,
        data,
        curProp: "tel",
        sameKey: "age"
      });
    }
  },
  {
    title: "Phone",
    dataIndex: "phone",
    onCell: (record, rowIndex) => {
      // 没有加第四个参数 age,没有前置条件所以会合并当前列相同项
      return mergeRows({ record, rowIndex, data, curProp: "phone" });
    }
  },
  {
    title: "Address",
    dataIndex: "address",
    onCell: (record, rowIndex) => {
      // 不管前面的age,现在以Home Phone必须相同,也能生效
      return mergeRows({
        record,
        rowIndex,
        data,
        curProp: "address",
        sameKey: "tel"
      });
    }
  }
];

const App: React.FC = () => (
  <Table columns={columns} dataSource={data} bordered />
);

export default App;

如果想对第一列的 Name 相同值也做合并,需要在并且在 name 列加上 onCell,并且 rowSpanRecord 对象中需要加上 name 属性

{
  title: "Name",
  dataIndex: "name",
  onCell: (record, rowIndex) => {
    // 没有前置条件所以会合并当前列相同项
    return mergeRows({ record, rowIndex, data, curProp: "name" });
  }
}

图例:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
我们调用这个方法还得写一个全局变量,不能直接通过引用的方式,将上面的 rowSpanRecord 封装进 mergeRows 方法里面,就可以让他以工具方法的形式进行引用调用了,封装代码:

/**
 * 合并目标列中相同值的行
 * @param record antd 提供的当前行数据
 * @param rowIndex antd 提供的当前行的序号
 * @param data 当前table的数据列表 tableData
 * @param curProp 当前需要合并行的列的属性值
 * @param sameKey 可选, 当前要合并行的列的前置条件,就是前面必须那个属性值必须相同才判断当前列相同合并
 * @returns {rowSpan: num}, 返回一个包含要合并的行数的对象
 */
const mergeRows = () => {
  // 合并行的数据记录
  let rowSpanRecord = {};
  return ({
    record = {},
    rowIndex = 0,
    data = [],
    curProp = "",
    sameKey = undefined,
  } = {}) => {
    // 往rowSpanRecord中添加数据
    if (!rowSpanRecord[sameKey] && sameKey) {
      rowSpanRecord[sameKey] = {};
    }
    // 往rowSpanRecord中添加数据
    if (!rowSpanRecord[curProp]) {
      rowSpanRecord[curProp] = {};
    }
    // console.log(rowSpanRecord, 'sdf', curProp, !!sameKey)
    // 当前行数据,当前列的值
    const recordCurPropValue = record[curProp];
    // 判断是否是重复渲染(antd刷新或者第一次渲染会触发两次onCell回调)
    if (
      rowIndex === 0 &&
      rowSpanRecord[curProp][recordCurPropValue]?.rowIndexs?.length > 0
    ) {
      for (const key of Object.keys(rowSpanRecord)) {
        rowSpanRecord[key] = {};
      }
    }
    // 记录中,当前要合并的列的prop,下面值的集合
    const rowSpanRecordCurPorp = rowSpanRecord[curProp];

    /**
       * 前面行已经进行了判断并合并,前面的行合并了后面的行之后,(该判断是后续行的判断)
      后面的行不能再显示了否则table会变形,所以这里需要将后续的列中返回 {rowSpan:0}
       */
    const curPropIndexs = rowSpanRecordCurPorp[recordCurPropValue]?.rowIndexs;
    if (curPropIndexs?.includes(rowIndex)) {
      return { rowSpan: 0 };
    }
    let rowSpan = 0;
    for (let i = rowIndex; i < data.length; i++) {
      // 有 sameKey,需要判断前置条件列中的值必须相同
      if (sameKey) {
        if (
          record[sameKey] === data[i][sameKey] &&
          recordCurPropValue === data[i][curProp]
        ) {
          rowSpan += 1;
          // 将列中行的值记录
          if (rowSpanRecordCurPorp[recordCurPropValue]) {
            rowSpanRecordCurPorp[recordCurPropValue].rowIndexs.push(i);
          } else {
            rowSpanRecordCurPorp[recordCurPropValue] = {
              rowIndexs: [i],
            };
          }
        } else {
          break;
        }
      } else {
        // 没有前置列,只需要判断当前列相邻列是否相同
        if (recordCurPropValue === data[i][curProp]) {
          rowSpan += 1;
          // 将列中行的值记录
          if (rowSpanRecordCurPorp[recordCurPropValue]) {
            rowSpanRecordCurPorp[recordCurPropValue].rowIndexs.push(i);
          } else {
            rowSpanRecordCurPorp[recordCurPropValue] = {
              rowIndexs: [i],
            };
          }
        } else {
          break;
        }
      }
    }
    return { rowSpan };
  };
};

调用方式:

// 合并行函数,在组件外面调用就行
const mergeTableRow = mergeRows()

// 组件内部的columns选项对象
{
  title: "Name",
  dataIndex: "name",
  onCell: (record, rowIndex) => {
    // 没有前置条件所以会合并当前列相同项
    return mergeTableRow({ record, rowIndex, data, curProp: "name" });
  }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

刷LeetCode:冒泡排序详解 【2/1000 第二题】含imagemagick动态效果图

&#x1f464;作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 作者专栏每日更新&#xff1a; LeetCode解锁1000题: 打怪升级之旅 LeetCode解锁1000题: 打怪升级之旅htt…

鸿蒙OS开发实例:【ArkTS类库多线程I/O密集型任务开发】

使用异步并发可以解决单次I/O任务阻塞的问题&#xff0c;但是如果遇到I/O密集型任务&#xff0c;同样会阻塞线程中其它任务的执行&#xff0c;这时需要使用多线程并发能力来进行解决。 I/O密集型任务的性能重点通常不在于CPU的处理能力&#xff0c;而在于I/O操作的速度和效率。…

代码随想录第25天 | 组合总和||| 、 电话号码的字母组合

一、前言 参考文献&#xff1a;代码随想录 今天的还是回溯算法&#xff0c;主要用到了昨天的回溯组合方法和巧妙思路方法&#xff0c;让我们继续为算法打基础吧&#xff01; 二、组合总和||| 1、思路&#xff1a; 这一题和昨日的组合没啥太大区别只是遍历的范围变为了固定…

2024 年高效开发的 React 生态系统

要使用 React 制作应用程序&#xff0c;需要熟悉正确的库来添加您需要的功能。例如&#xff0c;要添加某个功能&#xff08;例如身份验证或样式&#xff09;&#xff0c;您需要找到一个好的第三方库来处理它。 在这份综合指南中&#xff0c;我将向您展示我建议您在 2024 年使用…

VS2019连接MySQL

VS2019连接MySQL 下载MySQL Connector/C配置头文件&#xff0c;库文件路径配置头文件路径配置库的路径复制dll文件 MySQL的用户设置将权限赋值给新用户 编写代码往数据库写入 老师布置的作业让我们用VS2019连接MySQL实现一个小型的日志系统&#xff0c;中间踩了很多的坑&#x…

神经网络与深度学习(一)误差反传BP算法

误差反传BP算法 1多层感知机1.1XOR问题1.2多层感知机 2.BP算法2.1简述2.2详解2.2.1输入输出模型2.2.2梯度下降算法迭代2.2.3前向传播在输出端计算误差2.2.4误差反传--输出层2.2.5误差反传--隐含层2.2.6误差反传--总结 1多层感知机 1.1XOR问题 线性不可分问题&#xff1a; 无法…

C++心决之命名空间、重载函数和引用

目录 1. C关键字(C98) 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 3. C输入&输出 4. 缺省参数 4.1 缺省参数概念 4.2 缺省参数分类 5. 函数重载 5.1 函数重载概念 5.2 C支持函数重载的原理--名字修饰(name Mangling) 6. 引用 6.1 引用概念 6.2 引用特性…

Java多线程:定位死锁

检测死锁可以使用jconsole工具&#xff0c;或使用jps定位进程id&#xff0c;再用jstack定位死锁 方案1&#xff1a; 1. 先用jps查看所有的java进程id 2. jstack 进程id定位死锁 3. 查看死锁结果 方案2:从jdk的安装路径中找到bin目录, 点击jconsole

Linux线程原理以及基础操控函数使用(1)

线程原理 这一个课程的笔记 LWP&#xff1a;light weight process 轻量级的进程&#xff0c;本质仍是进程(在Linux环境下) 进程&#xff1a;独立地址空间&#xff0c;拥有PCB 线程&#xff1a;有独立的PCB&#xff0c;但没有独立的地址空间(共享) 区别&#xff1a;在于是否共…

算法6.4-6.6DFS

一个不知名大学生&#xff0c;江湖人称菜狗 original author: Jacky Li Email : 3435673055qq.com Time of completion&#xff1a;2024.03.27 Last edited: 2024.03.27 目录 算法6.4-6.6DFS 第1关&#xff1a;算法6.5采用邻接矩阵表示图的深搜 任务描述 相关知识 编程要求…

数据结构——优先级队列及多服务台模拟系统的实现

一、优先级队列的定义和存储 优先级队列定义&#xff1a;优先级高的元素在队头&#xff0c;优先级低的元素在队尾 基于普通线性表实现优先级队列&#xff0c;入队和出队中必有一个时间复杂度O(n),基于二叉树结构实现优先级队列&#xff0c;能够让入队和出队时间复杂度都为O(log…

RabbitMQ--04--发布订阅模式 (fanout)-案例

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 发布订阅模式 (fanout)---案例前言RabbitListener和RabbitHandler的使用 1.通过Spring官网快速创建一个RabbitMQ的生产者项目2.导入项目后在application.yml文件中配…

Python编程-并发编程基础梳理与高级特性案例讲解

Python编程-并发编程基础梳理与高级特性案例讲解 同步、异步通信与IO阻塞 同步&#xff08;Synchronous&#xff09;和异步&#xff08;Asynchronous&#xff09;通信是两种不同的通信方式&#xff0c;涉及到处理任务的时序和相互等待的关系。同步通信简单直观&#xff0c;但可…

Unity 使用TrailRenderer制作拖尾效果

使用TrailRenderer实现拖尾效果&#xff0c;具体操作步骤如下&#xff1a; 1、创建一个空对象 在Unity场景中创建一个空对象 2、添加TrailRenderer组件 选择步骤1创建的空对象&#xff0c;然后在Inspector面板中点击“Add Component”按钮&#xff0c;搜索并添加TrailRende…

蓝桥杯第七届大学B组详解

目录 1.煤球数量&#xff1b; 2.生日蜡烛&#xff1b; 3.凑算式 4.方格填数 5.四平方和 6.交换瓶子 7.最大比例 1.煤球数量 题目解析&#xff1a;可以根据题目的意思&#xff0c;找到规律。 1 *- 1个 2 *** 3个 3 ****** 6个 4 ********** 10个 不难发现 第…

C++入门知识详细讲解

C入门知识详细讲解 1. C简介1.1 什么是C1.2 C的发展史1.3. C的重要性1.3.1 语言的使用广泛度1.3.2 在工作领域 2. C基本语法知识2.1. C关键字(C98)2.2. 命名空间2.2 命名空间使用2.2 命名空间使用 2.3. C输入&输出2.4. 缺省参数2.4.1 缺省参数概念2.4.2 缺省参数分类 2.5. …

Linux - 第三节

改变用户类型 su 仅单纯的进行身份变化 依旧处于普通用户里面 su - 进行重新登录更改身份 退出用exit / ctrld su 用户名 改成成其他身份 对一条命令进行提权 sudo command r:可读 w:可写 x:可执行 -:对应的权限位置&#xff0c;没有权限 去掉所有权限 chmod u…

Qt中继承QCheckBox的类结合QTableWidget实现多选并且每个多选的id都不一样

1.相关描述 继承QCheckBox的类MyCheckBox&#xff0c;利用QTableWidget的setCellWidget方式添加MyCheckBox类的对象 2.相关页面 3.相关代码 mycheckbox.h #ifndef MYCHECKBOX_H #define MYCHECKBOX_H#include <QCheckBox> #include <QObject>class MyCheckBox : pu…

Elvis Presley 英文阅读

Elvis Presley 官方翻译 One of the most popular American singers of thetwentieth century was Elvis Presley. He made theRock & Roll music popular around the world. He sold millions of records and made manysuccessful films, and he helped change the dir…

基于SSM医院病历管理系统

基于SSM医院病历管理系统的设计与实现 摘要 病历管理系统是医院管理系统的重要组成,在计算机技术快速发展之前&#xff0c;病人或者医生如果想记录并查看自己的健康信息是非常麻烦的&#xff0c;因为在以往病人的健康信息通常只保存在自己的病历卡或者就诊报告中&#xff0c;…