element-table的合并行的使用-指定某些字段允许相邻数据能进行合并,通过传递的key键进行判断-公共方法
合并条件:相邻行中指定字段(如
contractId
)值相同的单元格需要合并。动态控制:仅合并标记了
rowSpan: true
的列(如合同名称、签订日期)。数据预处理:
排序数据:确保数据按合并字段(如
contractId
)排序,否则合并会错乱。
二次封装的element-table组件:
<ElTableref="table"v-loading="loading":data="dataStore":border="border":size="size":row-key="keyField":span-method="spanMethod"height="100%":show-overflow-tooltip="true"style="width: 100%; height: 100%"@selection-change="handleSelectionChange"@select-all="handleSelectAll"@sort-change="handleSortChange"@cell-click="handleCellClick"@row-click="handleRowClick"@header-dragend="handleColumnResize"><!-- 多选列 --><ElTableColumnv-if="isCheckBox"type="selection"width="55"fixed="left"align="center"/><!-- 自定义多选列 --><ElTableColumnv-if="mainRowSpanChoose"width="60"fixed="left"align="center"><template #header><ElCheckbox:indeterminate="indeterminate":model-value="checkAll"@change="onCheckBoxWhole"/></template><template #default="{ row }"><ElCheckbox:model-value="isCheck(row)"@change="(val) => onCheckBox({ target: { checked: val } }, row)"/></template></ElTableColumn><!-- 动态列 --><ElTableColumnv-for="column in columns":key="column.dataIndex":prop="column.dataIndex":field="column.dataIndex":label="column.title || column.name":width="column.width":min-width="column.minWidth || 50":fixed="column.fixed":sortable="column.sortable ? 'custom' : false":resizable="column.resizable":show-overflow-tooltip="column.showOverflowTooltip":align="column.align || 'left'"><template v-if="getColumnSlotName(column, 'header')" #header="scope"><slot :name="getColumnSlotName(column, 'header')" v-bind="scope" /></template><template #default="scope"><slotv-if="getColumnSlotName(column, 'default')":name="getColumnSlotName(column, 'default')"v-bind="scope"/><template v-else-if="column?.cellRender?.name"><ElFileRenderv-if="column?.cellRender?.name === 'k-list-file'":row="scope.row":column="column"/><ElExpressRenderv-if="column?.cellRender?.name === 'k-list-express'":row="scope.row":column="column"/><ElMapRenderv-if="column?.cellRender?.name === 'k-list-map'":row="scope.row":column="column"/><ElUserRenderv-if="column?.cellRender?.name === 'k-list-user'":row="scope.row":column="column"/><ElAssociatedItemRenderv-if="column?.cellRender?.name === 'k-list-association-attr'":row="scope.row":column="column"/></template><template v-else><template v-if="column?.formatter">{{ column.formatter(scope) || '-' }}</template><template v-else>{{ getNestedValue(scope.row, column.dataIndex) || '-' }}</template></template></template></ElTableColumn></ElTable>
合并行的方法:
// 在组件中计算spanMethod的地方修改为:
const spanMethod = computed(() => {const { spanMethod } = useTableSpan(dataStore.value,props?.mergedKey,columns.value,props.isCheckBox, // 是否有普通多选框props.mainRowSpanChoose, // 是否有自定义多选框)return spanMethod
})
公共方法useTableSpan:
/*** Element Table 相邻行合并公共方法(支持多选框合并)* @param {Array} data 表格数据(需按合并字段排序)* @param {string} mergeField 合并依据的字段(如 'contractId')* @param {Array} columns 列配置(标记需合并的列为 rowSpan: true)* @param {boolean} hasCheckbox 是否包含多选框列* @param {boolean} hasCustomCheckbox 是否包含自定义多选框列* @returns {object} { spanMap, spanMethod }*/
export function useTableSpan(data, mergeField, columns, hasCheckbox = false, hasCustomCheckbox = false) {// 1. 计算合并信息:{ 行索引: 合并行数 }const calculateSpans = () => {const spanMap = {}let lastValue = nulllet spanStartIndex = 0let currentSpan = 0data.forEach((row, index) => {// 处理空数据情况if (!data.length)return spanMapconst currentValue = row[mergeField]if (currentValue !== lastValue) {// 新分组,记录上一组的合并范围if (lastValue !== null) {for (let i = spanStartIndex; i < index; i++) {spanMap[i] = currentSpan}}spanStartIndex = indexlastValue = currentValuecurrentSpan = 1}else {currentSpan++}})// 处理最后一组if (data.length > 0 && lastValue !== null) {for (let i = spanStartIndex; i < data.length; i++) {spanMap[i] = currentSpan}}return spanMap}// 3. 辅助方法:找到合并的起始行const findMergeStartRow = (rowIndex) => {if (!data || data.length === 0)return rowIndexlet start = rowIndexwhile (start > 0 && data[start][mergeField] === data[start - 1][mergeField]) {start--}return start}// 初始化合并信息const spanMap = calculateSpans()// 2. 生成 span-method 函数const spanMethod = ({ row, column, rowIndex, columnIndex }) => {// 处理空数据情况if (!data || data.length === 0)return [1, 1]// 检查是否是多选框列(普通多选或自定义多选)const isCheckboxColumn = hasCheckbox && columnIndex === 0|| hasCustomCheckbox && (hasCheckbox ? columnIndex === 1 : columnIndex === 0)// 检查是否是需要合并的列const field = column.propertyconst columnConfig = columns.find(col => col.prop === field)const isMergeColumn = columnConfig?.rowSpan// 需要合并的列(包括多选框列和标记了rowSpan的列)if (isCheckboxColumn || isMergeColumn) {const spanCount = spanMap[rowIndex]const isStartOfGroup = rowIndex === findMergeStartRow(rowIndex)// 情况1:是合并组的起始行,且需要合并多行if (spanCount > 1 && isStartOfGroup) {return [spanCount, 1] // 合并行}// 情况2:是合并组的非起始行else if (spanCount > 1 && !isStartOfGroup) {return [0, 0] // 隐藏被合并的行}// 情况3:不需要合并的行(spanCount <= 1)else {return [1, 1] // 正常显示}}// 不需要合并的列return [1, 1]}return { spanMap, spanMethod, findMergeStartRow }
}