实现表格列的动态设置:列的显示/隐藏/排列顺序(element-table二次封装)

683 阅读3分钟

概要:本文主要记录一下参加工作遇到的第一个开发需求的解决方案以及过程中的问题及解决办法。

—— 需求

当表格的字段太多会出现滚动条,隐藏部分内容,而不同用户关注的列字段不一样。因此让用户可以对关注的字段进行选择展示,并确定字段的展示及顺序。

—— 分析思路

element中的table没有这个功能,所以要在原有的table上进行功能增强和组件封装。
主要实现步骤:

  • 使用下拉菜单展示和隐藏所有的字段;
  • 灵活地利用tree组件的拖拽属性,实现对列的排序和选择

—— 具体实现

此功能由两个组件搭配进行实现:ColumnSetting.vueCustomTable.vue,拆分成两个组件具有灵活性强的优点。两个组件都接受父组件的columns。

ColumnSetting.vue 组件中,要完成用户选择列选项显示的时候联动页面进行动态更新。利用传入的参数columns是数组类型,在本子组件中列设置的修改影响到父组件列设置的数据,进而影响到页面更新。所以在父组件中要对列设置进行监听,当列设置一变化就修改传入影响CustomTable.vue变化的props参数值(由于table的更新可能不及时,所以要为table设置key值,只要key值变化就会引起页面的更新)。

<template>
  <!--列设置按钮-->
  <el-dropdown trigger="click">
    <el-tooltip class="item" effect="dark" content="列设置" placement="top">
      <i class="el-icon-s-operation" />
    </el-tooltip>
    <el-dropdown-menu slot="dropdown">
      <span class="title">列设置</span>
      <el-tree
        draggable
        :data="columns"
        :props="defaultProps"
        :allow-drop="allowDrop"
        @node-drop="handleUpdate"
      >
        <span slot-scope="{ node , data}">
          <el-switch v-model="data.show" @change="handleUpdate" />
          <span>{{ node.label }}</span>
        </span>
      </el-tree>
      <el-dropdown-item>
        <div class="bt">
          <el-button type="primary" size="mini" @click="handleSave">保存</el-button>
          <el-button type="info" size="mini" @click="handleReset">重置</el-button>
        </div>
      </el-dropdown-item>
    </el-dropdown-menu>
  </el-dropdown>
</template>
<script>
export default {
  name: 'ColumnSetting',
  props: {
    columns: {
      type: [],
      default: null
    }
  },
  data() {
    return {
      // 列设置中 tree配置
      defaultProps: {
        children: 'children',
        label: 'label'
      }
    }
  },
  created() {
  },
  methods: {
    // 仅允许Tree节点上下拖动
    allowDrop(draggingNode, dropNode, type) {
      return type !== 'inner'
    },
    // Tree 拖动时更新表格
    handleUpdate() {
      const val = JSON.stringify(this.columns)
      this.$emit('update', val)
    },
    // 保存表格配置
    handleSave() {
      this.$emit('save')
    },
    // 重置表格设置
    handleReset() {
      this.$emit('reset')
    }
  }
}
</script>

表格组件主要设置了一个右侧固定列,动态列的渲染都依赖父组件传入的props(列设置:colSetting)。

<template>
  <!--表格-->
  <el-table
    :key="tableKey"
    :data="tableData"
    :border="false"
    fit
    stripe
    :highlight-current-row="false"
    :show-header="true"
    style="width: 100%"
    @sort-change="handleSortChange"
  >
    <!--      <el-table-column align="center" label="序号" type="index" />-->
    <template v-for="item in columns">
      <!-- 动态列 -->
      <el-table-column
        v-if="item.show"
        :key="item.prop"
        :show-overflow-tooltip="showOverflowTooltip"
        :prop="item.prop"
        :align = "item.align"
        :sortable="sortable || item.sortable"
        :label="item.label"
        :min-width="item.minWidth"
        :width="item.width"
        :resizable="item.resizable"
        @sort-change="handleSortChange"
      >
        <template slot-scope="scope">
          <slot :name="item.prop" :scope="scope.row[item.prop]" :row="scope.row">
            <span v-if="scope.row[item.prop]">{{ scope.row[item.prop] }}</span>
            <span v-else></span>
          </slot>
        </template>
      </el-table-column>
    </template>
    <!-- 固定列 -->
    <el-table-column v-if="fixColumn" fixed="right" align="center" label="操作" :width="fixWidth">
      <template slot-scope="scope">
        <slot name="operate" :scope="scope" :row="scope.row">
          <el-button type="success" size="mini">操作</el-button>
        </slot>
      </template>
    </el-table-column>
  </el-table>
</template>

<script>
export default {
  name: 'CustomTable',
  props: {
    tableData: {
      type: [],
      default: null
    },
    columns: {
      type: [],
      default: null
    },
    fixColumn: {
      type: Boolean,
      default: false
    },
    fixWidth: {
      type: Number,
      default: 100
    },
    showOverflowTooltip: {
      type: Boolean,
      default: true
    },
    sortable: {
      type: String,
      default: ''
    },
    tableKey: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
    }
  },
  methods: {
    handleSortChange({ column, prop, order }) {
      this.$emit('sort', { column, prop, order })
    }
  }
}
</script>

—— 过程遇到的问题

1. 重置更新

重置按钮功能:当点击重置按钮的时候,所有的选项都恢复到默认的状态。

问题:点击重置按钮的时候中遇到了列表无法正确更新的问题。

原因:深浅拷贝的问题。初始化的时候要对列配置项进行判断,在本地没有存储数据的情况下就要拷贝默认配置项(columns)作为用户操作的配置项(colSetting),数组和对象的拷贝有深浅拷贝的区别。数组和对象拷贝的是地址,所以用户的每次修改都会影响到默认配置项,于是重置的时候就会以上次用户操作之后的列表状态作为默认配置为重置赋值。

深拷贝问题的实现:在父组件的配置中进行列设置columns的配置,并进行深拷贝。

利用插件解决:

npm i --save lodash
import _ from 'lodash'
.....
this.colSetting = _.cloneDeep(this.columns) // 深拷贝默认配置项

2. 固定列的高度出现偏差,没有贴合表格

固定列是独立于表格体存在的,高度需要动态计算,在样式中加入如下设置可解决:

<style>
  .el-table__fixed-right{
    height: 100% !important;
  }
</style>

—— 总结

由于需要第一次封装组件,所以刚开始插槽和参数的设计考虑得并不是很完善。也好在改造的页面很多,且每个页面的展示有一定的差异性,于是在改造过程中对组件进行了不断地完善。

独立思考的过程感觉非常棒,但是遇到实在不懂的问题,抓破脑袋都想不通的时候,百度吧!百度真的啥都有,百度实在都找不到的话,就来掘金试试!哈哈哈哈哈~

开发会遇到很多问题,学习和工作中就是需要不断解决问题,升级打怪!