给你的table页面低成本的加上column隐藏控制器

687 阅读1分钟

给你的table页面低成本的加上column隐藏控制器

最近接到一个需求,背景是用户反馈table的column普遍都太多了,不同的用户群体关注的是不同的column,他们期望可以自定义控制显示哪些column。

这个需求并不复杂,用v-if控制就可以轻松实现,可怕的是这样的页面有上百个,每个页面可能包含20-40个不等的column,如果每个column都用一个v-if语句来控制,实现起来也太不优雅了。

实现

思路核心

由于存量的代码太多,想要最低成本的实现,最好是不用怎么修改原来的那些标签和属性,直接在入口文件中注册一个同名的组件覆盖掉原来的table组件,下面的代码都用element库来示例

import CtrlTable from '@/components/CtrlTable'
Vue.component('el-table', CtrlTable)

接下来就来编写CtrlTable这个组件,我们的核心诉求是尽量少的修改存量的代码,所以这里要先将attr和事件监听透传下来

CtrlTable.vue

import { Table } from 'element-ui'

export default {
  name: 'CtrlTable',
  components: {
    'origin-table': Table
  },
  render (h) {
    return h('origin-table', {
      attrs: this.$attrs,
      ref: 'table',
      on: this.$listeners
    }, this.$slots.default)
  }
}

上面组件中,从element-ui中引入了Table组件,然后在render函数中直接返回,这个组件的意义其实是作为一个中间组件,它接受旧代码中已经写在el-table标签里的attrs和事件监听,然后再把它们传递到真正的el-table组件中。到这一步,可以在页面中观察包含el-table的页面,它们已经正常出现在浏览器里了,看起来和之前并无区别,实际上中间多包了一层。

补全实现

接下来补充控制语句,实际上已经很简单了,只要对$slots.default来做过滤就可以了,接收一个checked的props,它表示要显示哪些column

CtrlTable.vue

import { Table } from 'element-ui'

export default {
  name: 'CtrlTable',
  components: {
    'origin-table': Table
  },
  props: {
    checked: {
      type: [Array, undefined],
      default: undefined
    }
  },
  computed: {
    columns () {
      if (!this.checked) {
        return this.$slots.default
      }
      return this.$slots.default.filter(item => (
        this.checked.includes(item.componentOptions && item.componentOptions.propsData.label) ||
        !(item.componentOptions && item.componentOptions.propsData.label) ||
        (item.componentOptions && item.componentOptions.propsData.label === '操作')
      ))
    }
  },
  render (h) {
    return h('origin-table', {
      attrs: this.$attrs,
      ref: 'table',
      on: this.$listeners
    }, this.columns)
  }
}

注意有一个小细节是如果不存在checked,会渲染所有的$slots.default,这么做的好处就是如果你有些页面不需要做column控制,只要不传checked,仍然可以渲染出所有的column

接下来就是实现控制器了,抽成了一个mixin,逻辑比较简单,直接贴代码

columnsCtrl.js

import Setting from '@/components/Setting'
const CONDITION_STORAGE_KEY = 'localstorage_key_demo'

export default {
  components: {
    Setting
  },

  data: () => ({
    tableCondition: [],
    tableChecked: []
  }),

  mounted () {
    this.__initCondition()
  },

  methods: {
    __initCondition () {
      const {
        table
      } = this.$refs
      if (table && table.$slots && table.$slots.default.length) {
        this.tableCondition = table.$slots.default.map(item => item.componentOptions && item.componentOptions.propsData.label).filter(item => !!item && item !== '操作')
      }
      this.__checkView(this.__getCache('table'))
    },

    __checkView (checked) {
      const { table } = this.$refs
      this.tableChecked = checked || table.$slots.default.map(item => item.componentOptions && item.componentOptions.propsData.label).filter(item => !!item && item !== '操作')
    },

    __getCache () {
      const { name } = this.$route
      const cacheKey = `${name}`
      const cache = JSON.parse(localStorage.getItem(CONDITION_STORAGE_KEY))
      if (cache && cache[cacheKey]) {
        return cache[cacheKey]
      }
      return null
    },

    handleSettingShow (cb) {
      const cache = this.__getCache()
      cb && cb(cache || [...this.tableCondition])
    },

    handleSettingSave ({ cb, checked }) {
      const { name } = this.$route
      const cacheKey = name
      let cache = JSON.parse(localStorage.getItem(CONDITION_STORAGE_KEY))
      if (!cache) {
        cache = {}
      }
      cache[cacheKey] = [...checked]
      localStorage.setItem(CONDITION_STORAGE_KEY, JSON.stringify(cache))
      this.__checkView(checked)
      cb && cb()
    }
  }
}

控制器组件部分代码比较简单,这里就省略了

demo

完整代码