vue3+ts+elementui-表格根据相同值合并

代码

复制代码
 <div style="height: auto; overflow: auto">
        <el-table ref="dataTableRef" v-loading="loading" :data="pageData" highlight-current-row border
          @selection-change="handleSelectionChange" :span-method="objectSpanMethod" :row-style="tableRowStyle">
          <el-table-column type="selection" width="55" align="center" />
          <!-- <el-table-column key="id" label="id" prop="id" align="center" /> -->
          <el-table-column key="roomName" label="房间名称" prop="roomName" align="center" />
          <el-table-column key="categoryName" label="所属分类" prop="categoryName" align="center" />
          <el-table-column key="deviceName" label="设备名称" prop="deviceName" align="center" />
        </el-table>

        <pagination v-if="total > 0" v-model:total="total" v-model:page="queryParams.pageNum"
          v-model:limit="queryParams.pageSize" @pagination="handleQuery()" style="float: right;" />
      </div>

js

复制代码
interface User {
  id: string
  roomName: string
  deviceName: string
  categoryName: string
}
interface SpanMethodProps {
  row: User
  column: TableColumnCtx<User>
  rowIndex: number
  columnIndex: number
}
const objectSpanMethod = ({ row, column, rowIndex }) => {
  if (column.property === 'roomName') {
    const currentRoom = row.roomName;
    let prevRoom = rowIndex === 0 ? null : pageData.value[rowIndex - 1].roomName;
    if (rowIndex === 0 || currentRoom !== prevRoom) {
      let count = 1;
      for (let i = rowIndex + 1; i < pageData.value.length; i++) {
        if (pageData.value[i].roomName === currentRoom) {
          count++;
        } else {
          break;
        }
      }
      return { rowspan: count, colspan: 1 };
    } else {
      return { rowspan: 0, colspan: 0 };
    }
  }
  if (column.property === 'categoryName') {
    const currentCategory = row.categoryName;
    let prevCategory = rowIndex === 0 ? null : pageData.value[rowIndex - 1].categoryName;
    if (rowIndex === 0 || currentCategory !== prevCategory) {
      let count = 1;
      for (let i = rowIndex + 1; i < pageData.value.length; i++) {
        if (pageData.value[i].categoryName === currentCategory) {
          count++;
        } else {
          break;
        }
      }
      return { rowspan: count, colspan: 1 };
    } else {
      return { rowspan: 0, colspan: 0 };
    }
  }
  return { rowspan: 1, colspan: 1 };
}
// 定义合并信息的类型
type SpanInfo = {
  rowspan: number
  colspan: number
}

// 用于存储 roomName 和 categoryName 的合并信息
const roomNameSpanMap: Map<number, SpanInfo> = new Map() // key 是 rowIndex
const categoryNameSpanMap: Map<number, SpanInfo> = new Map()

// 计算合并信息的方法
const calculateSpans = (data: User[]) => {
  let roomNameCount = 1
  let categoryNameCount = 1

  let prevRoomName = data[0]?.roomName
  let prevCategoryName = data[0]?.categoryName

  // 第一行默认 rowspan 为后续相同项的数量
  roomNameSpanMap.set(0, { rowspan: 1, colspan: 1 })
  categoryNameSpanMap.set(0, { rowspan: 1, colspan: 1 })

  for (let i = 1; i < data.length; i++) {
    const current = data[i]
    const prev = data[i - 1]

    // 处理 roomName
    if (current.roomName === prev.roomName) {
      roomNameCount++
      // 当前行不显示,rowspan 设为 0
      roomNameSpanMap.set(i, { rowspan: 0, colspan: 0 })
    } else {
      // 新的 roomName,设置 rowspan 为累计的数量
      roomNameSpanMap.set(i - 1, { rowspan: roomNameCount, colspan: 1 })
      roomNameCount = 1 // 重置计数
      roomNameSpanMap.set(i, { rowspan: 1, colspan: 1 }) // 当前行可能是新的开始
    }

    // 处理 categoryName
    if (current.categoryName === prev.categoryName) {
      categoryNameCount++
      categoryNameSpanMap.set(i, { rowspan: 0, colspan: 0 })
    } else {
      categoryNameSpanMap.set(i - 1, { rowspan: categoryNameCount, colspan: 1 })
      categoryNameCount = 1
      categoryNameSpanMap.set(i, { rowspan: 1, colspan: 1 })
    }

    prevRoomName = current.roomName
    prevCategoryName = current.categoryName
  }

  // 处理最后一行的 rowspan
  roomNameSpanMap.set(data.length - 1, { rowspan: roomNameCount, colspan: 1 })
  categoryNameSpanMap.set(data.length - 1, { rowspan: categoryNameCount, colspan: 1 })
}
const tableRowStyle = ({ row, rowIndex }) => {
  return {
    height: '40px',
    'vertical-align': 'middle'
  }
}

style

复制代码
// 表格
/* 确保表格行高一致,避免合并后行高错乱 */
:deep(.el-table .el-table__row) {
  /* 不要设置height/min-height/line-height,自动撑开 */
  vertical-align: middle !important;
}

:deep(.el-table__cell) {
  vertical-align: middle !important;
}

/* 确保表格容器有足够的空间 */
:deep(.table-container) {
  height: auto;
  /* 自动高度 */
  overflow: auto;
  /* 如果内容超出,显示滚动条 */
}
相关推荐
这是个栗子4 小时前
【问题解决】npm包下载速度慢
前端·npm·node.js
Komorebi_99994 小时前
数组和对象的深拷贝和浅拷贝的方法
前端·web
吃手机用谁付的款5 小时前
HTML常见标签
前端·html
好好研究5 小时前
CSS样式中的布局、字体、响应式布局
前端·css
拉不动的猪7 小时前
前端小白之 CSS弹性布局基础使用规范案例讲解
前端·javascript·css
伍哥的传说7 小时前
React强大且灵活hooks库——ahooks入门实践之开发调试类hook(dev)详解
前端·javascript·react.js·ecmascript·hooks·react-hooks·ahooks
界面开发小八哥7 小时前
界面控件Kendo UI for Angular 2025 Q2新版亮点 - 增强跨设备的无缝体验
前端·ui·界面控件·kendo ui·angular.js
枷锁—sha8 小时前
从零掌握XML与DTD实体:原理、XXE漏洞攻防
xml·前端·网络·chrome·web安全·网络安全
F2E_Zhangmo8 小时前
基于cornerstone3D的dicom影像浏览器 第二章,初始化页面结构
前端·javascript·vue·cornerstone3d·cornerstonejs