基于vue3+tsx,一文搞懂单元格合并原理前言 最近开发的一个移动端项目使用的技术栈是vue3+vant4,需求开发中
前言 最近开发的一个移动端项目使用的技术栈是vue3+vant4,需求开发中需要以表格的形式展示信息,在vant官方文档找了半天没看到table组件,移动端就不该展示表格?再重新引入一个ui框架有点小题大作,于是动手封装一下表格组件,table组件常用功能的封装在上一篇文章已经讲过了,本篇文章主要分享的是单元格合并功能。
实现效果
合并行
列合并
仓库地址
Gitee: gitee.com/sz-liunian/…
需求分析
elementui表格合并实现方法:
通过给table
传入span-method
方法可以实现合并行或列,方法的参数是一个对象,里面包含当前行row
、当前列column
、当前行号rowIndex
、当前列号columnIndex
四个属性。该函数可以返回一个包含两个元素的数组,第一个元素代表rowspan
,第二个元素代表colspan
。 也可以返回一个键名为rowspan
和colspan
的对象。
我们也参照elementui绑定一个方法mergeCellMethods
来实现单元格合并
使用方法
<table-list
:columns="titleList"
:data="dataList"
showBorder
@mergeCellMethods="mergeCellMethods"
>
<template #slotDate="{item, titleItem}">
<div class="user-info">
<span
class="ellipsis"
>{{ titleItem.title }}</span>
</div>
</template>
</table-list>
<script setup lang="ts">
import Mock from 'mockjs'
import { useRouter } from 'vue-router'
const router = useRouter()
import tableList from '@/components/tablePro/index.vue'
import { setData } from '@/utils/getTableData.ts'
const titleList = [
{
title: '日期',
width: 100,
key: 'date',
ellipsis: true,
fixed: 'left',
slotHeader: 'slotDate'
},
{
title: '基本信息',
key: 'baseInfo',
children: [
{
title: '姓名',
width: 95,
key: 'name',
sort: 2,
fieldName: 1,
align: 'right',
commaSplice: true,
// formatter
},
{
title: 'other',
key: 'other',
children: [
{
title: '年龄',
width: 95,
key: 'age',
sort: 2,
fieldName: 1,
align: 'right'
},
{
title: '公司',
width: 95,
key: 'company',
sort: 2,
fieldName: 1,
align: 'right'
},
]
}
]
},
{
title: '岗位',
width: 95,
key: 'work',
sort: 2,
fieldName: 1,
align: 'right',
commaSplice: true,
},
{
title: '城市',
width: 130,
key: 'city',
align: 'right',
commaSplice: true,
sort: 0,
fieldName: 3,
ellipsis: true,
// fixed: 'right',
}
]
const dataObj = {
date: '@date',
name: "@cname",
age: '@integer(24, 65)',
company: '@pick(["阿里", "腾讯", "字节"])',
work: '@pick(["产品", "运营", "ui", "前端", "后端", "测试", "运维"])',
city: '@city()'
}
const dataList = setData(Mock.mock('@natural(50, 100)'), dataObj)
// 单元格合并
const mergeCellMethods = function(row, column, rowIndex, columnIndex) {
// 合并单元格行
if (rowIndex % 2 === 0) {
if (columnIndex === 1) {
return [1, 2];
} else if (columnIndex === 2) {
return [0, 0];
}
}
}
// 合并单元格列
// const mergeCellMethods = function(row, column, rowIndex, columnIndex) {
// if (columnIndex === 1) {
// if (rowIndex % 2 === 0) {
// return {
// rowspan: 2,
// colspan: 1
// };
// } else {
// return {
// rowspan: 0,
// colspan: 0
// };
// }
// }
// }
</script>
实现原理
list.vue文件更改tdCell方法,使用hasMergeCellMethods方法之前要先判断一下该方法是否存在,具体实现请查看源码
// 是否有单元格合并方法
const hasMergeCellMethods = getValueType(attrs?.onMergeCellMethods) === 'Function'
// 单元格样式
const tdCell = (v: Object, rowIndex: Number) => {
return props.columns.map((item, index, arr) => {
let [rowspan, colspan] = [1, 1]
// 单元格合并方法
if (hasMergeCellMethods) {
let spans = [1, 1]
let columnIndex = index
if (props.fixedCol === 'center') {
// 中间列的索引需要加上左侧固定的列数
columnIndex = props.fixedLeftLength + index
} else if (props.fixedCol === 'right') {
// 右侧列的索引要加上中间滚动列数和左侧固定的列数
columnIndex = props.fixedLeftLength + props.centerColumnLength + index
}
spans = attrs.onMergeCellMethods(v, item, rowIndex, columnIndex) || [1, 1]
if (getValueType(spans) === 'Array') {
rowspan = spans[0]
colspan = spans[1]
} else if (getValueType(spans) === 'Object') {
rowspan = spans.rowspan
colspan = spans.colspan
}
}
if (rowspan === 0 && colspan === 0) {
return false
}
let val = ![null, undefined, ''].includes(v[item.key]) ? v[item.key] : ''
return (
<td
key={item.key}
rowspan={rowspan}
colspan={colspan}
class={[
'li-cell',
item.key,
{ 'show-border': props.showBorder },
{ 'border-bottom': props.borderBottom },
{ 'column-last': item.isLastCol }
]}
style={{
'text-align': item.align || 'left'
}}
onClick={e => emit('handlerCellClick', v, item, e)}
>
{cellContent(item, v, index)}
</td>
)
})
}
Table 属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
showTitle | 是否展示表头 | boolean | true |
showList | 是否展示列表 | boolean | true |
columns | 表格列的配置描述见下表 | array | [] |
data | 列表数据 | array | [] |
showBorder | 是否显示单元格边框 | boolean | false |
isHeaderFixed | 表头是否吸顶 | boolean | true |
headerFixedTopValue | 页面划动,表头吸顶top值 | Number | 88 (单位px) |
height | 行高度 | number、string | 50(单位px) |
columns属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
title | 表头展示内容 | string (支持传html) | '' |
width | 单元格宽度 | number | 70(单位px) |
widthAuto | 宽度自适应,会把剩余宽度按比例分配给设置了该字段的列 | boolean | false |
key | 当前列要展示的字段 | string 每一个 表头选项,key要保持惟一 | -(无默认值) |
fixed | 列是否固定,可选 'left' 'right' | string | -(无默认值) |
align | 单元格对齐方式, 可选 left、center、 right | string | left |
ellipsis | 超过宽度将自动省略 | boolean | false |
slotHeader | title 插槽key,可使用此字段添加插槽 | string | - |
headerBackground | 表头背景色 | string | '#fff' |
formatter | 列表单元格格式化函数 | Function(h, row, column, index) | - |
util | 单位 | number、string |
插槽: 表头插槽使用 columns 属性 slotHeader自定义表头内容 内容插槽使用 columns 属性key自定义单元格内容
事件
参数 | 说明 | 类型 |
---|---|---|
handlerClick | row 点击事件 | Function(row, index) |
handlerCellClick | cell 点击事件 | Function(row, column) |
mergeCellMethods | 合并单元格方法,用法同element-ui | function(v, item, rowIndex, columnIndex) |
转载自:https://juejin.cn/post/7425603008414154763