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 分钟前
Vue3 Hooks:逻辑复用的解决方案
前端·javascript·vue.js·前端框架·vue
S***t7148 分钟前
前端物联网开发
前端·物联网
IT·小灰灰12 分钟前
深度解析重排序AI模型:基于硅基流动API调用多语言重排序AI实战指南
java·大数据·javascript·人工智能·python·数据挖掘·php
我叫张小白。15 分钟前
Vue3 Props 的使用:组件间数据传递的桥梁
前端·javascript·vue.js·vue3
r***869816 分钟前
Nginx解决前端跨域问题
运维·前端·nginx
广州华水科技23 分钟前
单北斗GNSS在桥梁变形监测中的关键应用与技术优势分析
前端
IT_陈寒24 分钟前
Python 3.12新特性实战:10个让你效率翻倍的代码优化技巧
前端·人工智能·后端
z***948425 分钟前
Redis 6.2.7安装配置
前端·数据库·redis
2301_8072886326 分钟前
MPRPC项目制作(第四天)
java·服务器·前端
J***793927 分钟前
前端在移动端中的React Native Windows
前端·react native·react.js