vue3+ant design实现表格相同内容合并为一行

时间:2024-04-30 07:21:07

近期在处理数据报表项目过程中频繁面临表格合并的挑战。产品经理希望能将表格中重复内容并合至同一行显示。尽管通过查阅众多博文了解相关方法与实践,但实际操作却无法解决相关问题。例如,提及ant design版本不适配或添加相关代码仍未成功;更为糟糕的是,即使成功合并,数据仍然仅可水平居中,无法实现垂直居中。作为技术支持者,必须满足产品需求,力求保持99%的原型匹配度。虽然工作压力较大,但我坚信这些困难并非无法克服。所以,我也将这些经验分享出来,供大家共同探讨学习。

一、开发页面、获取表格数据

 在使用ant design组件时需要进行提前安转,这个具体安装步骤在ant design官网中有,不会的小伙伴可以去查看一下,但是版本尽量和vue匹配,不然后续在写代码时就会发现有很多都无法使用的问题。安装完组件后就可以正常使用了,下面是具体详细代码:

(1)<template>部分

<template>
  <!-- 表格部分 -->
  <div class="content">
    <div class="table-body">
      <a-table
        :columns="columns"
        :dataSource="tableData"
        :pagination="false"
        bordered
        :scroll="{ y: 'calc(100vh - 520px)' }"
      />
    </div>
    <!-- 分页 -->
    <div class="pagination">
      <a-pagination
        :current="current"
        :size="size"
        :total="pageData.total"
        @change="onChange"
        show-less-items
        show-quick-jumper
        show-size-changer
        :show-total="(total, range) => `共 ${pageData.total || 0} 条`"
      />
    </div>
  </div>
</template>

(2)<script>部分

<script setup>
import { getRoadManageTotal} from '../api/index'
// 分页信息
const pageData = ref({})
// 当前页
const current = ref(1)
// 多少页入参
const size = ref('')
const tableData = ref([])
const columns = ref([
  {
    title: '日期',
    dataIndex: 'tabdate',
    key: 'tabdate',
    align: 'center'
  },
  {
    title: '路线',
    dataIndex: 'tabroad',
    key: 'road',
    align: 'center',
  },
  {
    title: '路段',
    dataIndex: 'tabhighway',
    key: 'highway',
    align: 'center'
  },
  {
    title: '断面流量(万辆)',
    dataIndex: 'tabdmflow',
    key: 'dmflow',
    align: 'center'
  },
  {
    title: '清障统计',
    // dataIndex: 'tabwrecker',
    // key: 'wrecker',
    // align: 'center',
    children: [
      {
        title: '合计(起)',
        dataIndex: 'wreckertotal',
        align: 'center',
        key: 'total',
        width: 100,
        sorter: true
      },
      {
        title: '放空(起)',
        dataIndex: 'wreckvent',
        align: 'center',
        key: 'vent'
      }
    ]
  },
  {
    title: '交通事故(起)',
    dataIndex: 'tabtrafficacci',
    key: 'trafficacci',
    align: 'center'
  }
])

// 获取表格数据
const getTabData = ()=> {
  return new Promise((resolve, reject) => {
    getRoadManageTotal()
      .then(res => {
        tableData.value = []
        if (res.code === 200 && res.data) {
          const dataList = res.data.list
          dataList.forEach((item, index) => {
            console.log(item, 'ieieiei')
            if (item.hj === 'null' && item.fk === 'null' && item.jtsg === 'null') {
              tableData.value.push({
                tabdate: item.createDate,
                tabroad: item.road,
                tabhighway: item.sectionName,
                tabdmflow: item.dmll,
                wreckertotal: 0,
                wreckvent: 0,
                // tabwrecker: item.qzzy,
                tabtrafficacci: 0
              })
            } else {
              tableData.value.push({
                tabdate: item.createDate,
                tabroad: item.road,
                tabhighway: item.sectionName,
                tabdmflow: item.dmll,
                wreckertotal: item.hj,
                wreckvent: item.fk,
                // tabwrecker: item.qzzy,
                tabtrafficacci: item.jtsg
              })
            }
          })
          // console.log(res.data, '数据是:')
          pageData.value = {
            ...pageData.value,
            total: res.data.total,
            showTotal: () => `共 ${res.data.total || 0} 条`
            // current: res.data.page.currentPage
          }
        } else {
          tableData.value = []
        }
        resolve(res.data)
      })
      .catch(error => {
        reject(error)
      })
  })
}
onMounted(() => {
  getTabData()
})
</script>

二、合并行

在需要合并行的表格头部添加customCell,并写入以下代码:

customCell: (record, rowIndex, column) => {
      return {
        rowSpan: rowSpanArr.value[rowIndex],
        style: {
          'text-align': 'center', // 单元格文本居中
          'vertical-align': 'middle' // 单元格内容垂直居中
        }
      }
    }

 在操作完以上步骤之后,重新再定义一个合并行的函数,名为getRowspan,具体代码如下:

//合并行
const getRowspan = (dataScroce, filed) => {
  // console.log(dataScroce, 'dataScrocedataScroce')
  console.log(filed, 'filedfiledfiled')
  let spanArr = []
  let position = 0
  dataScroce.forEach((item, index) => {
    if (index === 0) {
      spanArr.push(1)
      // spanArr.splice(2, 0, 1)
      position = 0
    } else {
      //需要合并的地方判断
      if (dataScroce[index][filed] === dataScroce[index - 1][filed]) {
        spanArr[position] += 1
        spanArr.push(0)
        // spanArr.splice(2, 0, 0)
      } else {
        spanArr.push(1)
        position = index
        console.log(position, ' position position')
      }
    }
  })
  return spanArr
}

我这里需要合并的是时期和路线,所以需要再重新定义两个变量,

//定义合并的时期变量
const rowSpanArr = ref([])
const roadSpanArr = ref([])

操作完以上步骤后,对于定义的这些如何使用呢,这里需要将合并的两个字段进行处理,所以我们在获取表格数据的地方进行操作,

 rowSpanArr.value = getRowspan(tableData.value, 'tabdate')
 roadSpanArr.value = getRowspan(tableData.value, 'tabroad')

这样就完成了相应的合并啦。

三、结果展示、完整代码

可能看完以上代码有些小伙伴还是有些迷糊,下面将完整代码分享给大家吧!

<template>
  <!-- 表格部分 -->
  <div class="content">
    <div class="table-body">
      <a-table
        :columns="columns"
        :dataSource="tableData"
        :pagination="false"
        bordered
        :scroll="{ y: 'calc(100vh - 520px)' }"
      />
    </div>
    <!-- 分页 -->
    <div class="pagination">
      <a-pagination
        :current="current"
        :size="size"
        :total="pageData.total"
        @change="onChange"
        show-less-items
        show-quick-jumper
        show-size-changer
        :show-total="(total, range) => `共 ${pageData.total || 0} 条`"
      />
    </div>
  </div>
</template>

<script setup>
import { getRoadManageTotal} from '../api/index'
// 分页信息
const pageData = ref({})
// 当前页
const current = ref(1)
// 多少页入参
const size = ref('')
const tableData = ref([])
//合并行
const getRowspan = (dataScroce, filed) => {
  // console.log(dataScroce, 'dataScrocedataScroce')
  console.log(filed, 'filedfiledfiled')
  let spanArr = []
  let position = 0
  dataScroce.forEach((item, index) => {
    if (index === 0) {
      spanArr.push(1)
      // spanArr.splice(2, 0, 1)
      position = 0
    } else {
      //需要合并的地方判断
      if (dataScroce[index][filed] === dataScroce[index - 1][filed]) {
        spanArr[position] += 1
        spanArr.push(0)
        // spanArr.splice(2, 0, 0)
      } else {
        spanArr.push(1)
        position = index
        console.log(position, ' position position')
      }
    }
  })
  return spanArr
}
//定义合并的时期变量
const rowSpanArr = ref([])
const roadSpanArr = ref([])
const columns = ref([
  {
    title: '日期',
    dataIndex: 'tabdate',
    key: 'tabdate',
    align: 'center',
    customCell: (record, rowIndex, column) => {
      return {
        rowSpan: rowSpanArr.value[rowIndex],
        style: {
          'text-align': 'center', // 单元格文本居中
          'vertical-align': 'middle' // 单元格内容垂直居中
        }
      }
    }
  },
  {
    title: '路线',
    dataIndex: 'tabroad',
    key: 'road',
    align: 'center',
    customCell: (record, rowIndex, column) => {
      return {
        rowSpan: roadSpanArr.value[rowIndex],
        style: {
          'text-align': 'center', // 单元格文本居中
          'vertical-align': 'middle' // 单元格内容垂直居中
        }
      }
    }
  },
  {
    title: '路段',
    dataIndex: 'tabhighway',
    key: 'highway',
    align: 'center'
  },
  {
    title: '断面流量(万辆)',
    dataIndex: 'tabdmflow',
    key: 'dmflow',
    align: 'center'
  },
  {
    title: '清障统计',
    // dataIndex: 'tabwrecker',
    // key: 'wrecker',
    // align: 'center',
    children: [
      {
        title: '合计(起)',
        dataIndex: 'wreckertotal',
        align: 'center',
        key: 'total',
        width: 100,
        sorter: true
      },
      {
        title: '放空(起)',
        dataIndex: 'wreckvent',
        align: 'center',
        key: 'vent'
      }
    ]
  },
  {
    title: '交通事故(起)',
    dataIndex: 'tabtrafficacci',
    key: 'trafficacci',
    align: 'center'
  }
])
// 获取表格数据
const getTabData = () => {
  return new Promise((resolve, reject) => {
    getRoadManageTotal()
      .then(res => {
        tableData.value = []
        if (res.code === 200 && res.data) {
          const dataList = res.data.list
          dataList.forEach((item, index) => {
            console.log(item, 'ieieiei')
            if (item.hj === 'null' && item.fk === 'null' && item.jtsg === 'null') {
              tableData.value.push({
                tabdate: item.createDate,
                tabroad: item.road,
                tabhighway: item.sectionName,
                tabdmflow: item.dmll,
                wreckertotal: 0,
                wreckvent: 0,
                // tabwrecker: item.qzzy,
                tabtrafficacci: 0
              })
            } else {
              tableData.value.push({
                tabdate: item.createDate,
                tabroad: item.road,
                tabhighway: item.sectionName,
                tabdmflow: item.dmll,
                wreckertotal: item.hj,
                wreckvent: item.fk,
                // tabwrecker: item.qzzy,
                tabtrafficacci: item.jtsg
              })
            }
            rowSpanArr.value = getRowspan(tableData.value, 'tabdate')
            roadSpanArr.value = getRowspan(tableData.value, 'tabroad')
          })
          // console.log(res.data, '数据是:')
          pageData.value = {
            ...pageData.value,
            total: res.data.total,
            showTotal: () => `共 ${res.data.total || 0} 条`
            // current: res.data.page.currentPage
          }
        } else {
          tableData.value = []
        }
        resolve(res.data)
      })
      .catch(error => {
        reject(error)
      })
  })
}
onMounted(() => {
  getTabData()
})
</script>

<style lang="less" scoped>
.search-accident {
  width: 100%;
  height: 12%;
  display: flex;
  align-items: center;
  background-color: #fff;
  .accident-date {
    padding: 0 10px 0 30px;
    font-size: 18px;
    // color: #929292;
  }
  .btn-list {
    display: flex;
    align-items: center;
    ::v-deep .ant-btn {
      line-height: 1.4vw;
      height: 2vw;
    }
  }
}
.content {
  width: 100%;
  height: 85%;
  margin-top: 30px;
  background-color: #fff;
  .content-top {
    display: flex;
    justify-content: flex-end;
    padding-top: 30px;
    padding-right: 30px;
    ::v-deep .ant-btn {
      line-height: 1.4vw;
      height: 2vw;
    }
  }
  .table-body {
    // width: 100%;
    height: 70%;
    margin-top: 2%;
    padding: 0 20px;
    box-sizing: border-box;
    background-color: #fff;
  }
  .pagination {
    // width: 100%;
    height: 10%;
    margin-top: 2%;
    background-color: #fff;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding-right: 30px;
  }
}
</style>