Element分析(组件篇)——TableHeader

说明

table-header是表头组件,较为复杂,直接看源码解读。

源码解读

import ElCheckbox from 'element-ui/packages/checkbox';
import ElTag from 'element-ui/packages/tag';
import Vue from 'vue';
import FilterPanel from './filter-panel.vue';

/**
 * 获取所有的列,因为会有嵌套所以使用了递归
 * @param columns 原始的所有列
 * @return 所有列
 */
const getAllColumns = (columns) => {
  const result = [];
  columns.forEach((column) => {
    if (column.children) {
      result.push(column);
      result.push.apply(result, getAllColumns(column.children));
    } else {
      result.push(column);
    }
  });
  return result;
};

/**
 * 将所有的列信息转换成表头的行信息
 * @param originColumns 列信息
 * @return 表头信息
 */
const convertToRows = (originColumns) => {
  // 最大层级
  let maxLevel = 1;
  // 遍历列来判断表头每个单元格需要占多少格
  const traverse = (column, parent) => {
    if (parent) {
      column.level = parent.level + 1;
      if (maxLevel < column.level) {
        maxLevel = column.level;
      }
    }
    if (column.children) {
      let colSpan = 0;
      column.children.forEach((subColumn) => {
        traverse(subColumn, column);
        colSpan += subColumn.colSpan;
      });
      column.colSpan = colSpan;
    } else {
      column.colSpan = 1;
    }
  };

  // 获取每一列的层级
  originColumns.forEach((column) => {
    column.level = 1;
    traverse(column);
  });

  const rows = [];
  for (let i = 0; i < maxLevel; i++) {
    rows.push([]);
  }

  const allColumns = getAllColumns(originColumns);

  // 相同的层级作为同一行
  allColumns.forEach((column) => {
    if (!column.children) {
      column.rowSpan = maxLevel - column.level + 1;
    } else {
      column.rowSpan = 1;
    }
    rows[column.level - 1].push(column);
  });

  return rows;
};

export default {
  name: 'ElTableHeader',

  // 渲染函数
  render(h) {
    // 原始列信息
    const originColumns = this.store.states.originColumns;
    // 表头信息
    const columnRows = convertToRows(originColumns, this.columns);

    return (
      // table 上的 style 是为了清除默认的间距
      
        {/* colgroup 是用来存储列信息的 */}
        {/* 列信息 */}
          {
            this._l(this.columns, column =>
              )
          }
          {/* 如果左侧有固定还需要记录滚动条的宽度 */}
          {
            !this.fixed && this.layout.gutterWidth
              ? : ''
          }
        
          {
            // 按行渲染
            this._l(columnRows, (columns, rowIndex) =>
              
              {
                // 渲染每个单元格
                this._l(columns, (column, cellIndex) =>
                  
                )
              }
              {
                // 弥补滚动条的宽度
                !this.fixed && this.layout.gutterWidth
                  ? 
                  : ''
              }
              
            )
          }
        
this.handleMouseMove($event, column) } // 鼠标移出事件 on-mouseout={ this.handleMouseOut } // 鼠标按下事件 on-mousedown={ ($event) => this.handleMouseDown($event, column) } // 鼠标单击事件 on-click={ ($event) => this.handleHeaderClick($event, column) } class={ [ column.id, column.order, column.headerAlign, column.className || '', // 判断是否隐藏,为了处理 fixed rowIndex === 0 && this.isCellHidden(cellIndex, columns) ? 'is-hidden' : '', // 判断是不是最后一级 !column.children ? 'is-leaf' : '' ] }>
0 ? 'highlight' : '' ] }> { // 渲染单元格内部的内容 column.renderHeader ? column.renderHeader.call( this._renderProxy, h, { column, $index: cellIndex, store: this.store, _self: this.$parent.$vnode.context }) : column.label } { // 渲染排序的标志 column.sortable ? this.handleSortClick($event, column) }> : '' } { // 渲染筛选器的箭头 column.filterable ? this.handleFilterClick($event, column) }> : '' }
); }, props: { fixed: String, store: { required: true }, layout: { required: true }, border: Boolean, defaultSort: { type: Object, default() { return { prop: '', order: '' }; } } }, components: { ElCheckbox, ElTag }, computed: { // 判断是不是全选了 isAllSelected() { return this.store.states.isAllSelected; }, // 判断总的列数 columnsCount() { return this.store.states.columns.length; }, // 左侧固定列数 leftFixedCount() { return this.store.states.fixedColumns.length; }, // 右侧固定列数 rightFixedCount() { return this.store.states.rightFixedColumns.length; }, // 所有的列 columns() { return this.store.states.columns; } }, created() { this.filterPanels = {}; }, mounted() { if (this.defaultSort.prop) { const states = this.store.states; // 排序的属性 states.sortProp = this.defaultSort.prop; // 升序或降序 states.sortOrder = this.defaultSort.order || 'ascending'; this.$nextTick(_ => { for (let i = 0, length = this.columns.length; i < length; i++) { let column = this.columns[i]; // 如果是要排序的属性 if (column.property === states.sortProp) { column.order = states.sortOrder; states.sortingColumn = column; break; } } if (states.sortingColumn) { this.store.commit('changeSortCondition'); } }); } }, beforeDestroy() { const panels = this.filterPanels; for (let prop in panels) { // 销毁全部的筛选面板 if (panels.hasOwnProperty(prop) && panels[prop]) { panels[prop].$destroy(true); } } }, methods: { // 判断单元格是否应当隐藏 isCellHidden(index, columns) { // 左侧固定的 wrapper 中,那么除了固定的这些列都应该隐藏掉 if (this.fixed === true || this.fixed === 'left') { return index >= this.leftFixedCount; } else if (this.fixed === 'right') { // 右侧固定的 wrapper 中,规定列之前的也都应当隐藏 let before = 0; for (let i = 0; i < index; i++) { before += columns[i].colSpan; } return before < this.columnsCount - this.rightFixedCount; } else { // 剩下的就是隐藏固定的列 return (index < this.leftFixedCount) || (index >= this.columnsCount - this.rightFixedCount); } }, // 切换全选 toggleAllSelection() { this.store.commit('toggleAllSelection'); }, // 处理点击筛选器,应当显示对应的筛选面板 handleFilterClick(event, column) { event.stopPropagation(); const target = event.target; const cell = target.parentNode; const table = this.$parent; // 查找对应的筛选面板 let filterPanel = this.filterPanels[column.id]; // 如果存在,并且打开了,就关闭它 if (filterPanel && column.filterOpened) { filterPanel.showPopper = false; return; } // 如果不存在,就创建它 if (!filterPanel) { filterPanel = new Vue(FilterPanel); this.filterPanels[column.id] = filterPanel; filterPanel.table = table; filterPanel.cell = cell; filterPanel.column = column; !this.$isServer && filterPanel.$mount(document.createElement('div')); } // 创建后打开 setTimeout(() => { filterPanel.showPopper = true; }, 16); }, // 处理表头点击事件 handleHeaderClick(event, column) { if (!column.filters && column.sortable) { // 排序 this.handleSortClick(event, column); } else if (column.filters && !column.sortable) { // 筛选 this.handleFilterClick(event, column); } this.$parent.$emit('header-click', column, event); }, // 鼠标按下 handleMouseDown(event, column) { if (this.$isServer) return; // 如果这一列还有孩子直接返回,应该操作孩子的宽度 if (column.children && column.children.length > 0) return; /* istanbul ignore if */ // 如果有拖拽的列,并且有边框 if (this.draggingColumn && this.border) { // 表示正在拖动 this.dragging = true; // 显示 resize this.$parent.resizeProxyVisible = true; const tableEl = this.$parent.$el; const tableLeft = tableEl.getBoundingClientRect().left; // 拖动列 const columnEl = this.$el.querySelector(`th.${column.id}`); const columnRect = columnEl.getBoundingClientRect(); const minLeft = columnRect.left - tableLeft + 30; columnEl.classList.add('noclick'); this.dragState = { startMouseLeft: event.clientX, // 鼠标开始位置 startLeft: columnRect.right - tableLeft, // 开始位置距离表格最左边的距离 startColumnLeft: columnRect.left - tableLeft, // 开始时列左边离表格最左边的距离 tableLeft }; // 显示拖拽位置的线 const resizeProxy = this.$parent.$refs.resizeProxy; resizeProxy.style.left = this.dragState.startLeft + 'px'; document.onselectstart = function() { return false; }; document.ondragstart = function() { return false; }; // 鼠标移动的时候,计算移动距离并且移动辅助线 const handleMouseMove = (event) => { const deltaLeft = event.clientX - this.dragState.startMouseLeft; const proxyLeft = this.dragState.startLeft + deltaLeft; resizeProxy.style.left = Math.max(minLeft, proxyLeft) + 'px'; }; // 鼠标抬起 const handleMouseUp = () => { if (this.dragging) { const finalLeft = parseInt(resizeProxy.style.left, 10); // 最终停止的位置 const columnWidth = finalLeft - this.dragState.startColumnLeft; // 应该变成的列宽 column.width = column.realWidth = columnWidth; // 应用列宽改变 this.store.scheduleLayout(); // 重新更新布局 document.body.style.cursor = ''; this.dragging = false; this.draggingColumn = null; this.dragState = {}; this.$parent.resizeProxyVisible = false; } // 移除相应的监听器 document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); document.onselectstart = null; document.ondragstart = null; setTimeout(function() { columnEl.classList.remove('noclick'); }, 0); }; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); } }, // 鼠标移动事件 handleMouseMove(event, column) { if (column.children && column.children.length > 0) return; let target = event.target; while (target && target.tagName !== 'TH') { // 寻找 th 标签 target = target.parentNode; } // 如果没有列,或者不能改变大小 if (!column || !column.resizable) return; // 如果正在拖动并且有边框 if (!this.dragging && this.border) { let rect = target.getBoundingClientRect(); const bodyStyle = document.body.style; if (rect.width > 12 && rect.right - event.pageX < 8) { bodyStyle.cursor = 'col-resize'; this.draggingColumn = column; } else if (!this.dragging) { bodyStyle.cursor = ''; this.draggingColumn = null; } } }, // 鼠标移除后 handleMouseOut() { if (this.$isServer) return; document.body.style.cursor = ''; }, // 切换排序顺序 toggleOrder(order) { return !order ? 'ascending' : order === 'ascending' ? 'descending' : null; }, // 点击排序 handleSortClick(event, column) { event.stopPropagation(); // 切换排序顺序 let order = this.toggleOrder(column.order); // 寻找 TH let target = event.target; while (target && target.tagName !== 'TH') { target = target.parentNode; } // 如果这时候有 `noclick` 类,就移除 if (target && target.tagName === 'TH') { if (target.classList.contains('noclick')) { target.classList.remove('noclick'); return; } } // 如果不能排序就直接返回 if (!column.sortable) return; const states = this.store.states; let sortProp = states.sortProp; let sortOrder; const sortingColumn = states.sortingColumn; // 如果排序列不是当前列,就切换成当前列 if (sortingColumn !== column) { if (sortingColumn) { sortingColumn.order = null; } states.sortingColumn = column; sortProp = column.property; } // 如果没有顺序 if (!order) { sortOrder = column.order = null; states.sortingColumn = null; sortProp = null; } else { sortOrder = column.order = order; } states.sortProp = sortProp; states.sortOrder = sortOrder; this.store.commit('changeSortCondition'); } }, data() { return { draggingColumn: null, dragging: false, dragState: {} }; } };

你可能感兴趣的:(Element分析(组件篇)——TableHeader)