element-plus 低版本table 固定列修改sticky布局

89 阅读2分钟

element-plus 低版本为例 低版本table中 固定列的实现是通过多个表格覆盖实现的,如果数据量大,表格会非常卡,新版本table采用sticky实现 这里通过修改源码实现低版本升级sticky形式

移出模版部分固定列代码

<div
      v-if="store.states.fixedColumns.value.length > 0"
      ref="fixedWrapper"
      v-mousewheel="handleFixedMousewheel"
      :style="[
        {
          width: layout.fixedWidth.value ? layout.fixedWidth.value + 'px' : ''
        },
        fixedHeight
      ]"
      class="el-table__fixed"
    >
      // .....
<div
      v-if="store.states.rightFixedColumns.value.length > 0"
      ref="rightFixedPatch"
      :style="{
        width: layout.scrollY.value ? layout.gutterWidth + 'px' : '0',
        height: layout.headerHeight.value + 'px'
      }"
      class="el-table__fixed-right-patch"
    ></div>

添加计算固定列的工具函数代码

在util文件中添加工具代码,都是用来计算是否是固定列的


function getCurrentColumns<T>(column: TableColumnCtx<T>): TableColumnCtx<T>[] {
  if (column.children) {
    return flatMap(column.children, getCurrentColumns)
  } else {
    return [column]
  }
}

function getColSpan<T>(colSpan: number, column: TableColumnCtx<T>) {
  return colSpan + column.colSpan
}

export const isFixedColumn = <T>(
  index: number,
  fixed: string | boolean,
  store: any,
  realColumns?: TableColumnCtx<T>[]
) => {
  let start = 0
  let after = index
  const columns = store.states.columns.value
  if (realColumns) {
    // fixed column supported in grouped header
    const curColumns = getCurrentColumns(realColumns[index])
    const preColumns = columns.slice(0, columns.indexOf(curColumns[0]))

    start = preColumns.reduce(getColSpan, 0)
    after = start + curColumns.reduce(getColSpan, 0) - 1
  } else {
    start = index
  }
  let fixedLayout
  switch (fixed) {
    case 'left':
      if (after < store.states.fixedLeafColumnsLength.value) {
        fixedLayout = 'left'
      }
      break
    case 'right':
      if (
        start >=
        columns.length - store.states.rightFixedLeafColumnsLength.value
      ) {
        fixedLayout = 'right'
      }
      break
    default:
      if (after < store.states.fixedLeafColumnsLength.value) {
        fixedLayout = 'left'
      } else if (
        start >=
        columns.length - store.states.rightFixedLeafColumnsLength.value
      ) {
        fixedLayout = 'right'
      }
  }
  return fixedLayout
    ? {
      direction: fixedLayout,
      start,
      after,
    }
    : {}
}


export const getFixedColumnsClass = <T>(
  index: number,
  fixed: string | boolean,
  store: any,
  realColumns?: TableColumnCtx<T>[],
  offset = 0
) => {
  const classes: string[] = []
  const { direction, start, after } = isFixedColumn(
    index,
    fixed,
    store,
    realColumns
  )
  if (direction) {
    const isLeft = direction === 'left'
    classes.push(`fixed-column--${direction}`)
    if (
      isLeft &&
      // @ts-ignore
      after + offset === store.states.fixedLeafColumnsLength.value - 1
    ) {
      classes.push('is-last-column')
    } else if (
      !isLeft &&
      // @ts-ignore
      start - offset ===
      store.states.columns.value.length -
      store.states.rightFixedLeafColumnsLength.value
    ) {
      classes.push('is-first-column')
    }
  }
  return classes
}

function getOffset<T>(offset: number, column: TableColumnCtx<T>) {
  return (
    offset +
    (column.realWidth === null || Number.isNaN(column.realWidth)
      ? Number(column.width)
      : column.realWidth)
  )
}

export const getFixedColumnOffset = <T>(
  index: number,
  fixed: string | boolean,
  store: any,
  realColumns?: TableColumnCtx<T>[]
) => {
  const {
    direction,
    start = 0,
    after = 0,
  } = isFixedColumn(index, fixed, store, realColumns)
  if (!direction) {
    return
  }
  const styles: any = {}
  const isLeft = direction === 'left'
  const columns = store.states.columns.value
  if (isLeft) {
    styles.left = columns.slice(0, start).reduce(getOffset, 0)
  } else {
    styles.right = columns
      .slice(after + 1)
      .reverse()
      .reduce(getOffset, 0)
  }
  return styles
}


export const ensurePosition = (style, key: string) => {
  if (!style) return
  if (!Number.isNaN(style[key])) {
    style[key] = `${style[key]}px`
  }
}

分别修改style.hepler.ts

分别修改header、footer、cell等部分的style.helper.ts文件

table-header

image.png

table-body

image.png

table-footer

if (isCellHidden(cellIndex, store.states.columns.value, column)) {
      classes.push(...
        getFixedColumnsClass(cellIndex, column.fixed, props.store)
      )
}

/table/src/table-body/render-helper.ts

CellClass部分多传递一个参数 image.png

原来模板的样式只覆盖到了body部分,导致滚动时,样式不能覆盖header,所以这里增加同步修改headerWrapper的className image.png

覆盖部分样式

样式部分需要做一下覆盖 这里左右固定列的class可以自定义,需要同步修改util中的类名

$shadowLeft: inset 10px 0 10px -10px rgba(0, 0, 0, .15)!important;
$shadowRight: inset -10px 0 10px -10px rgba(0, 0, 0, .15)!important;

.el-table {
  .fixed-column--left {
    position: sticky !important;
    background: inherit;
    z-index: 2;
    left: 0;
  }
  .fixed-column--right {
    position: sticky !important;
    background: inherit;
    z-index: 2;
    right: 0;
  }

  .el-table__body-wrapper,
  .el-table__header-wrapper,
  .el-table__footer-wrapper {
    width: 100%;
    tr {
      td,
      th {
        overflow: unset!important;
        &.fixed-column--left,
        &.fixed-column--right {
          position: sticky !important;
          z-index: 2;
          // background: getCssVar('bg-color');
          &.is-last-column,
          &.is-first-column {
            &::before {
              content: '';
              position: absolute;
              top: 0px;
              width: 10px;
              bottom: -1px;
              overflow-x: hidden;
              overflow-y: hidden;
              box-shadow: none;
              touch-action: none;
              pointer-events: none;
            }
          }
          &.is-first-column {
            &::before {
              left: -10px;
            }
          }
          &.is-last-column {
            &::before {
              right: -10px;
              box-shadow: none;
            }
          }
        }
      }
    }
  }

  .is-scrolling-right {
    .fixed-column--left.is-last-column {
      &::before {
        box-shadow: $shadowLeft;
      }
    }
    .fixed-column--left.is-last-column {
      border-right-color: transparent;
    }
  }

  .is-scrolling-middle {
    .fixed-column--left.is-last-column {
      border-right-color: transparent;
    }
    .fixed-column--right.is-first-column {
      &::before {
        box-shadow: $shadowRight;
      }
    }
    .fixed-column--left.is-last-column {
      &::before {
        box-shadow: $shadowLeft;
      }
    }
  }

  .is-scrolling-none {
    .fixed-column--left,
    .fixed-column--right {
      &.is-first-column,
      &.is-last-column {
        &::before {
          box-shadow: none;
        }
      }
    }

    th.fixed-column--left,
    th.fixed-column--right {
      background-color: getCssVar('header-bg-color');
    }
  }
}