diff --git a/packages/components/table/__tests__/table.test.ts b/packages/components/table/__tests__/table.test.ts
index 303cb5b6ae..41a36bd4ef 100644
--- a/packages/components/table/__tests__/table.test.ts
+++ b/packages/components/table/__tests__/table.test.ts
@@ -492,6 +492,68 @@ describe('Table.vue', () => {
wrapper.unmount()
})
+ it('cell mouse enter on cell of which rowSpan > 2', async () => {
+ const wrapper = mount({
+ components: {
+ ElTable,
+ ElTableColumn,
+ },
+ template: `
+
+
+
+
+
+
+
+ `,
+ data() {
+ return {
+ testData: getTestData(),
+ }
+ },
+ methods: {
+ objectSpanMethod({ rowIndex, columnIndex }) {
+ if (columnIndex === 0) {
+ if (rowIndex % 2 === 0) {
+ return {
+ rowspan: 2,
+ colspan: 1,
+ }
+ } else {
+ return {
+ rowspan: 0,
+ colspan: 0,
+ }
+ }
+ }
+ },
+ },
+ })
+ const vm = wrapper.vm
+ await doubleWait()
+ const cell = vm.$el
+ .querySelectorAll('.el-table__body-wrapper tbody tr')[0]
+ .querySelector('.el-table__cell')
+ triggerEvent(cell, 'mouseenter', true, false)
+ await doubleWait()
+ await rAF()
+ await doubleWait()
+ const row = vm.$el.querySelectorAll('.el-table__body-wrapper tbody tr')[1]
+ expect([...row.classList]).toContain('hover-row')
+ await doubleWait()
+ triggerEvent(cell, 'mouseleave', true, false)
+ await rAF()
+ await doubleWait()
+ expect([...row.classList]).not.toContain('hover-row')
+ wrapper.unmount()
+ })
+
it('cell-mouse-leave', async () => {
const wrapper = createTable('cell-mouse-leave')
await doubleWait()
@@ -950,6 +1012,69 @@ describe('Table.vue', () => {
wrapper.unmount()
})
+ it('hover on which rowSpan > 1', async () => {
+ const wrapper = mount({
+ components: {
+ ElTable,
+ ElTableColumn,
+ },
+ template: `
+
+
+
+
+
+
+
+ `,
+ data() {
+ return {
+ testData: getTestData(),
+ }
+ },
+ methods: {
+ objectSpanMethod({ rowIndex, columnIndex }) {
+ if (columnIndex === 0) {
+ if (rowIndex % 2 === 0) {
+ return {
+ rowspan: 2,
+ colspan: 1,
+ }
+ } else {
+ return {
+ rowspan: 0,
+ colspan: 0,
+ }
+ }
+ }
+ },
+ },
+ })
+ const vm = wrapper.vm
+ await doubleWait()
+ const rows = vm.$el.querySelectorAll('.el-table__body-wrapper tbody tr')
+ triggerEvent(rows[1], 'mouseenter', true, false)
+ await doubleWait()
+ await rAF()
+ await doubleWait()
+ const cell = vm.$el
+ .querySelectorAll('.el-table__body-wrapper tbody tr')[0]
+ .querySelector('.el-table__cell')
+
+ expect([...cell.classList]).toContain('hover-cell')
+ await doubleWait()
+ triggerEvent(rows[1], 'mouseleave', true, false)
+ await rAF()
+ await doubleWait()
+ expect([...cell.classList]).not.toContain('hover-cell')
+ wrapper.unmount()
+ })
+
it('highlight-current-row', async () => {
const wrapper = mount({
components: {
diff --git a/packages/components/table/src/table-body/events-helper.ts b/packages/components/table/src/table-body/events-helper.ts
index 34df83abff..3070d4f145 100644
--- a/packages/components/table/src/table-body/events-helper.ts
+++ b/packages/components/table/src/table-body/events-helper.ts
@@ -1,7 +1,7 @@
// @ts-nocheck
import { h, inject, ref } from 'vue'
import { debounce } from 'lodash-unified'
-import { hasClass } from '@element-plus/utils'
+import { addClass, hasClass, removeClass } from '@element-plus/utils'
import { createTablePopper, getCell, getColumnByCell } from '../util'
import { TABLE_INJECTION_KEY } from '../tokens'
import type { TableColumnCtx } from '../table-column/defaults'
@@ -60,6 +60,21 @@ function useEvents(props: Partial>) {
bottom: paddingBottom,
}
}
+
+ const toggleRowClassByCell = (
+ rowSpan: number,
+ event: MouseEvent,
+ toggle: (el: Element, cls: string) => void
+ ) => {
+ let node = event.target.parentNode
+ while (rowSpan > 1) {
+ node = node?.nextSibling
+ if (!node || node.nodeName !== 'TR') break
+ toggle(node, 'hover-row hover-fixed-row')
+ rowSpan--
+ }
+ }
+
const handleCellMouseEnter = (
event: MouseEvent,
row: T,
@@ -76,6 +91,9 @@ function useEvents(props: Partial>) {
cell,
namespace
)
+ if (cell.rowSpan > 1) {
+ toggleRowClassByCell(cell.rowSpan, event, addClass)
+ }
const hoverState = (table.hoverState = { cell, column, row })
table?.emit(
'cell-mouse-enter',
@@ -143,7 +161,9 @@ function useEvents(props: Partial>) {
const handleCellMouseLeave = (event) => {
const cell = getCell(event)
if (!cell) return
-
+ if (cell.rowSpan > 1) {
+ toggleRowClassByCell(cell.rowSpan, event, removeClass)
+ }
const oldHoverState = parent?.hoverState
parent?.emit(
'cell-mouse-leave',
diff --git a/packages/components/table/src/table-body/index.ts b/packages/components/table/src/table-body/index.ts
index 0df091f514..fa80c25102 100644
--- a/packages/components/table/src/table-body/index.ts
+++ b/packages/components/table/src/table-body/index.ts
@@ -28,18 +28,54 @@ export default defineComponent({
useRender(props)
const { onColumnsChange, onScrollableChange } = useLayoutObserver(parent!)
+ const hoveredCellList = []
watch(props.store.states.hoverRow, (newVal: any, oldVal: any) => {
+ const el = instance?.vnode.el as HTMLElement
+ const rows = Array.from(el?.children || []).filter((e) =>
+ e?.classList.contains(`${ns.e('row')}`)
+ )
+
+ // hover rowSpan > 1 choose the whole row
+ let rowNum = newVal
+ const childNodes = rows[rowNum]?.childNodes
+ if (childNodes?.length) {
+ const indexes = Array.from(childNodes).reduce((acc, item, index) => {
+ // drop colsSpan
+ const pre = childNodes[index - 1]?.colSpan > 1
+ const next = childNodes[index + 1]?.colSpan > 1
+ if (item.nodeName !== 'TD' && !pre && !next) {
+ acc.push(index)
+ }
+ return acc
+ }, [])
+
+ indexes.forEach((rowIndex) => {
+ while (rowNum > 0) {
+ // find from previous
+ const preChildNodes = rows[rowNum - 1]?.childNodes
+ if (
+ preChildNodes[rowIndex] &&
+ preChildNodes[rowIndex].nodeName === 'TD'
+ ) {
+ addClass(preChildNodes[rowIndex], 'hover-cell')
+ hoveredCellList.push(preChildNodes[rowIndex])
+ break
+ }
+ rowNum--
+ }
+ })
+ } else {
+ hoveredCellList.forEach((item) => removeClass(item, 'hover-cell'))
+ hoveredCellList.length = 0
+ }
if (!props.store.states.isComplex.value || !isClient) return
rAF(() => {
// just get first level children; fix #9723
- const el = instance?.vnode.el as HTMLElement
- const rows = Array.from(el?.children || []).filter((e) =>
- e?.classList.contains(`${ns.e('row')}`)
- )
const oldRow = rows[oldVal]
const newRow = rows[newVal]
- if (oldRow) {
+ // when there is fixed row, hover on rowSpan > 1 should not clear the class
+ if (oldRow && !oldRow.classList.contains('hover-fixed-row')) {
removeClass(oldRow, 'hover-row')
}
if (newRow) {
diff --git a/packages/theme-chalk/src/table.scss b/packages/theme-chalk/src/table.scss
index a6f5a33bf9..6bc7e93cda 100755
--- a/packages/theme-chalk/src/table.scss
+++ b/packages/theme-chalk/src/table.scss
@@ -556,6 +556,10 @@
}
}
+ tr > td.hover-cell {
+ background-color: getCssVar('table-row-hover-bg-color');
+ }
+
tr.current-row > td.#{$namespace}-table__cell {
background-color: getCssVar('table-current-row-bg-color');
}