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

发布于:2025-07-18 ⋅ 阅读:(19) ⋅ 点赞:(0)

代码

 <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;
  /* 如果内容超出,显示滚动条 */
}


网站公告

今日签到

点亮在社区的每一天
去签到