Element-ui Table 组件二次封装 - 实现列筛选

153 阅读2分钟

index.vue

<template>
  <el-table
    v-if="show"
    ref="txTable"
    :data="data"
    v-bind="$attrs"
    @select="(selection, row) => $emit('select', selection, row)"
    @select-all="selection => $emit('select-all', selection)"
    @selection-change="selection => $emit('selection-change', selection)"
    @cell-click="(row, column, cell, event) => $emit('cell-click', row, column, cell, event)"
    @cell-dblclick="(row, column, cell, event) => $emit('cell-dblclick', row, column, cell, event)"
    @row-click="(row, column, event) => $emit('row-click', row, column, event)">
    <slot name="header" />
    <template v-for="(item, index) of columns">
      <template v-if="item.show">
        <!-- 是否为多级表头 这里只考虑二级 -->
        <el-table-column v-if="item.children && item.children.length" :key="index" v-bind="item.attr">
          <TxTableColumn v-for="(subItem, subIndex) of item.children" :key="subIndex + '0' + index" :item="subItem" />
        </el-table-column>
        <TxTableColumn v-else :key="index" :item="item.attr" />
      </template>
    </template>
    <slot />
  </el-table>
</template>

<script>
import TxTableColumn from './txTableColumn'
export default {
  name: 'TxTable',
  components: { TxTableColumn },
  props: {
    data: { type: Array, default: () => [] },
    columns: { type: Array, default: () => [] },
  },
  data() {
    return { show: true }
  },
}
</script>
  • 通过 v-bind="$attrs" 绑定 props 传递过来的所有属性
  • 通过 $emit 将时间绑定到附件上,实现跟 element-ui table 一样的事件监听
  • 这里只考虑 二级表头
  • 通过 v-if="show" 来重新渲染表格,避免更多筛选后,table 错位问题

txTableColumn.vue

<template>
  <el-table-column v-if="item.render" v-bind="item">
    <template slot-scope="scope">
      <TxTableSolt :render="item.render" :row="scope.row" :index="scope.$index" :column="item" />
    </template>
  </el-table-column>
  <el-table-column v-else v-bind="item" />
</template>

<script>
import TxTableSolt from './txTableSolt'

export default {
  components: { TxTableSolt },
  props: {
    item: { type: Object, required: true },
  },
}
</script>
  • 这里自定义 TxTableSolt 实现 定义 render 函数

txTableMore.vue

<template>
  <div class="tx-table-more clearfix">
    <span>{{ label }}</span>
    <el-popover
      v-model="isPannel"
      placement="bottom-end"
      title="请选择展示的字段"
      :width="width"
      trigger="click"
      popper-class="tx-table-pannel">
      <i slot="reference" :class="icon" class="more-filter" />
      <div class="oper-pannel">
        <el-checkbox v-model="checkAll" label="全选" :indeterminate="isIndeterminate" @change="allChange" />
        <ul>
          <li v-for="(item, index) of list" :key="index">
            <el-checkbox v-model="item.show" :label="item.attr.label" @change="itemChange" />
          </li>
        </ul>
        <div class="btn-wrap">
          <el-button @click="isPannel = false">取消</el-button>
          <el-button type="primary" @click="confirm">确定</el-button>
        </div>
      </div>
    </el-popover>
  </div>
</template>

<script>
import { cloneDeep } from 'lodash'
export default {
  props: {
    label: { type: String, default: '' },
    icon: { type: String, default: 'el-icon-more' },
    width: { type: Number, default: 200 },
    columns: { type: Array, required: true },
  },
  data() {
    return {
      list: [],
      isPannel: false,
      isIndeterminate: false,
      checkAll: true,
    }
  },
  created() {
    this.list = cloneDeep(this.columns.filter(e => e.more === true))
    this.itemChange()
  },
  methods: {
    /**
     * @description 全选处理
     */
    allChange(val) {
      this.checkAll = val
      this.isIndeterminate = false
      this.list.forEach(e => (e.show = val))
    },
    /**
     * @description 单项处理
     */
    itemChange() {
      this.isIndeterminate = this.list.some(e => e.show === true)
      !this.isIndeterminate && (this.checkAll = false)
      if (this.list.every(e => e.show === true)) {
        this.checkAll = true
        this.isIndeterminate = false
      }
    },
    /**
     * @description 确定
     */
    confirm() {
      this.list.forEach(e => {
        const item = this.columns.find(se => se.attr.label === e.attr.label)
        item.show = e.show
      })

      this.$parent.$parent.$parent.show = false
      this.$nextTick(() => {
        this.$parent.$parent.$parent.show = true
        // this.$parent.$parent.doLayout()
        this.isPannel = false
      })
    },
  },
}
</script>

<style lang="scss" scoped>
.tx-table-more {
  position: relative;
  .more-filter {
    float: right;
    margin-top: 4px;
    transform: rotate(90deg);
    font-size: 14px;
    cursor: pointer;
    &:hover {
      color: $themeColor;
    }
  }
}
</style>
  • 实现列筛选核心代码

txTableSolt.vue

<script>
export default {
  functional: true,
  props: {
    row: { type: Object, required: true },
    render: { type: Function, required: true },
    index: { type: Number, required: true },
    column: { type: Object, default: null },
  },
  render: (h, data) => {
    const props = data.props
    const params = {
      row: props.row,
      index: props.index,
    }
    if (props.column) params.column = props.column
    return props.render(params, h)
  },
}
</script>

tx-table 正确使用姿势

<template>
  <TxTable
    ref="queryTable"
    :data="tableData"
    :columns="columns"
    :height="800"
    @selection-change="val => (sectionList = val)">
    <!-- 注意:这里预留了一个 header 插槽,可以灵活配置 -->
    <template slot="header" />
    <!-- 注意:这里是一个默认插槽,方便更多配置-灵活多变 -->
    <el-table-column min-width="90" align="center" fixed="right">
      <!-- 注意:这里使用 更多列筛选配置 如不需要列筛选可不做配置 -->
      <template slot="header">
        <TxTableMore :columns="columns" label="操作" />
      </template>
      <template slot-scope="scope">
        <el-button type="text">确认</el-button>
        <el-button type="text">编辑</el-button>
        <el-button type="text">删除</el-button>
      </template>
    </el-table-column>
  </TxTable>
</template>

<script>
import TxTable from '@/components/table'
import TxTableMore from '@/components/table/txTableMore'
import columns from './components/columns'
export default {
  components: { TxTableMore, TxTable },
  data() {
    return {
      tableData: [], // 表格数据
      sectionList: [], // 选择到的数据
      columns: columns(this), // 列配置
    }
  },
}
</script>
// columns.js
import { currency, date, number, multiply } from '@/utils/filters'
import columnStatus from '../components/Status'

function columns(that) {
  return [
    { show: true, attr: { type: 'selection', width: 45, align: 'center', fixed: 'left' } },
    { show: true, attr: { type: 'index', label: '序号', width: 45, align: 'center', fixed: 'left' } },
    { more: true, show: true, attr: { label: '业务类型' }, 'min-width': 90, prop: 'bussTypeCode' },
    {
      more: true,
      show: true,
      attr: {
        label: '机构',
        'min-width': 200,
        render: ({ row }, h) => {
          if (that.auth.key) {
            return (
              <span class='table-link' onClick={() => that.levelHandler('机构', row)}>
                {row.orgCode} {row.orgName}
              </span>
            )
          } else {
            return (
              <span>
                {row.orgCode} {row.orgName}
              </span>
            )
          }
        },
      },
    },
    {
      more: true,
      show: true,
      attr: { label: '环比校验', align: 'center' },
      children: [
        {
          label: '数据',
          'min-width': 120,
          render: ({ row }, h) => {
            return <span>{number(row.hgjyValue)}</span>
          },
        },
        {
          label: '比列',
          'min-width': 80,
          render: ({ row }, h) => {
            return <span>{multiply(row.hbjyPercent)}</span>
          },
        },
      ],
    },
  ]
}

export default columns