element-plus el-table合并单元格 并封装成hook方法

1,288 阅读3分钟

el-table单元格合并

通过给 table 传入span-method方法可以实现合并行或列, 方法的参数是一个对象,里面包含当前行 row、当前列 column、当前行号 rowIndex、当前列号 columnIndex 四个属性。 该函数可以返回一个包含两个元素的数组,第一个元素代表 rowspan,第二个元素代表 colspan。 也可以返回一个键名为 rowspan 和 colspan 的对象。

效果展示

el-table合并单元格.png

实现方法

1. 函数参数:

  • tableData:指向包含表格数据的数组。
  • mergeArr:指向一个包含要合并的表格字段名的数组。
  • orderId:表示用于处理鼠标悬停高亮的分组名

2. 定义响应式变量

  • mergeObj:将存储有关每个列需要跨越多少单元格的信息。
  • currentIndex:用于存储当前鼠标悬停的索引,以进行鼠标交互。
  const mergeObj = ref({});
  const currentIndex  = ref('');

3. 实现合并行或列

此函数用于确定单元格应跨越多少行和列(合并)。它检查列的属性(字段名)是否在 mergeArr 中。如果是,则查看 mergeObj 来确定跨越的值。如果值大于 0,则表示单元格应该垂直跨越,如果值为 0,则表示由于合并应隐藏单元格。

/**
   * span-method属性对应方法 实现合并行或列
   * @param {*} param0 
   * @returns 
   */
  const objectSpanMethod = ({ column, rowIndex }) => {
    if(mergeArr.value.indexOf(column.property) !== -1) { 
      // 判断其值是不是为0 
      if(mergeObj.value[column.property][rowIndex]) { 
        return [mergeObj.value[column.property][rowIndex], 1]
      } else {
        // 如果为0则为需要合并的行
        return [0, 0];
      }
    }
  }

4. 行类名添加

解决高亮问题

/**
   * row-class-name属性对应方法
   * @param {*} row 
   * @returns 
   */
  const tableRowClassName = (row) => {
    if (row.row[orderId.value] == currentIndex.value) {
      return 'hover-bg'
    } else {
      return ''
    }
  };

5. 单元格鼠标交互处理函数:

/**
 * cell-mouse-enter属性对应方法
 * @param {*} row 
 */
const handleCellMouseEnter = (row) => {
  currentIndex.value = row[orderId.value];
};
/**
 * cell-mouse-leave属性对应方法
 */
const handleCellMouseLeave = () => {
  currentIndex.value = ''
};

完整代码

import { ref,watch } from "vue";
/**
* 合并相同的多列单元格
* @param {ref([])} tableData table数据
* @param {ref({})} mergeArr table字段名
* @param {ref('')} orderId 组名 可取table中数据相同的字段名,用于解决鼠标浮动高亮问题
* @returns 
*/
export default function spanMethod(tableData,mergeArr,orderId) {
    const mergeObj = ref({});
    const currentIndex  = ref('');

    watch([tableData,mergeArr],()=>{
      mergeArr.value.forEach((key) => {
        let count = 0; // 用来记录需要合并行的起始位置
        mergeObj.value[key] = []; // 记录每一列的合并信息
        tableData.value.forEach((item, index) => {
            // index == 0表示数据为第一行,直接 push 一个 1
            if(index === 0) {
                mergeObj.value[key].push(1); 
            } else {
                // 判断当前行是否与上一行其值相等 如果相等 在 count 记录的位置其值 +1 表示当前行需要合并 并push 一个 0 作为占位
                if(item[key] && item[key] === tableData.value[index - 1][key]) { 
                    mergeObj.value[key][count] += 1;
                    mergeObj.value[key].push(0);
                } else {
                    // 如果当前行和上一行其值不相等 
                    count = index; // 记录当前位置 
                    mergeObj.value[key].push(1); // 重新push 一个 1
                }
            }
        })
      })
    },{
      deep: true,
      immediate: true
    })
    /**
     * cell-mouse-enter属性对应方法
     * @param {*} row 
     */
    const handleCellMouseEnter = (row) => {
      currentIndex.value = row[orderId.value];
    };
    /**
     * cell-mouse-leave属性对应方法
     */
    const handleCellMouseLeave = () => {
      currentIndex.value = ''
    };
    /**
     * row-class-name属性对应方法
     * @param {*} row 
     * @returns 
     */
    const tableRowClassName = (row) => {
      if (row.row[orderId.value] == currentIndex.value) {
        return 'hover-bg'
      } else {
        return ''
      }
    };
    /**
     * span-method属性对应方法 实现合并行或列
     * @param {*} param0 
     * @returns 
     */
    const objectSpanMethod = ({ column, rowIndex }) => {
      if(mergeArr.value.indexOf(column.property) !== -1) { 
        // 判断其值是不是为0 
        if(mergeObj.value[column.property][rowIndex]) { 
          return [mergeObj.value[column.property][rowIndex], 1]
        } else {
          // 如果为0则为需要合并的行
          return [0, 0];
        }
      }
    }
    return {
      objectSpanMethod,
      handleCellMouseEnter,
      handleCellMouseLeave,
      tableRowClassName
    };
}

使用

<template>
    <el-table 
        :data="tableData" 
        :span-method="objectSpanMethod"
        border
        @cell-mouse-enter="handleCellMouseEnter"
        @cell-mouse-leave="handleCellMouseLeave"
        :row-class-name="tableRowClassName">
        <el-table-column label="详评阅标项" prop="subitemTitle" width="200" />
        <el-table-column label="详评阅标内容" prop="subitemContent" width="200" />
        <el-table-column label="专家阅标" prop="optionValue" width="200" />
        <el-table-column label="供应商应答情况" prop="answerData" width="200" />
        <el-table-column label="详评阅标项所在投标文件位置" prop="bidBookPage" width="200" />
    </el-table>
</template>

<script setup>
import spanMethod from "@/hooks/spanMethod";

const mergeArr = ref(['id','subitemTitle','subitemContent','optionValue','answerData']);
const orderId = ref('id');
const tableData = ref([
    {
        "id": "abc995d7918c4f4a9af7cabdb45e1b07",
        "expertBiddingId": null,
        "biddingSubitemId": "bs04",
        "templateSubitemId": "772759e0-7a74-42e1-b918-fc3c17d6f5ea",
        "subitemTitle": "商务文件编制质量",
        "subitemContent": "商务文件编制(按提交资料的完整性进行评定):优秀得8-10分,较好的得5-7分,一般的得0-4分",
        "answerOptions": "优秀(8-10分);较好(5-7分);一般(0-4分)",
        "answerOptionsRule": "优秀:最大值10:最小值8:默认值9;较好:最大值7:最小值:5:默认值6;一般:最大值4:最小值0:默认值2",
        "optionValue": null,
        "optionDesc": null,
        "scoreResult": null,
        "autoScore": null,
        "createUserId": null,
        "updateUserId": null,
        "delFlag": null,
        "pos": 2,
        "expertBiddingIds": null
    },
    ...
])
const {
  objectSpanMethod,
  handleCellMouseEnter,
  handleCellMouseLeave,
  tableRowClassName,
} = spanMethod(tableData,mergeArr,orderId);
</script>

<style scoped lang="scss">
:deep(.el-table__body .el-table__row.hover-bg td) {
  background-color: #F5F7FA;
}
</style>