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;
  /* 如果内容超出,显示滚动条 */
}
相关推荐
半个落月7 小时前
LangSmith 全链路观测(上):用 LangGraph 与 Milvus 构建可追踪的 RAG 客服
javascript·langchain
Java后端的Ai之路8 小时前
06-React-Props
前端·react.js·前端框架
半个落月8 小时前
LangChain.js Agent 中间件实战:生命周期、动态工具与文件权限
javascript·langchain
Flynt8 小时前
开源"间谍卫星"说数据全是真的,我把它的免key数据源挨个试了一遍
前端·开源·数据可视化
Csvn9 小时前
渲染管线
前端
程序员大阳9 小时前
副队长CSS教程(33)–圆角与透明
前端·css·透明·圆角
志尊宝9 小时前
Vue3 零基础每日笔记(082):文件上传实战——el-upload 校验、自定义上传、头像预览
前端·javascript·vue.js·笔记·#javascript·#vue.js
鬼手点金10 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
柳杉10 小时前
借助 Codex 和可视化工作流,从调研到完整实现,3D 大屏竟然可以这么简单
前端·ai编程·数据可视化
IMPYLH10 小时前
HTML 的 <wbr> 元素
前端·html