background

The system has a rectification requirement, requiring all tables in the system to support local dynamic column implicit, drag and drop the sequence position, fixed column function, involving a lot of pages

Upper effect picture:

Train of thought

In fact, the first thought must be the FORM of JSON configuration form, and then by the loop out of the column to control the corresponding position and attributes but! That’s a lot of pages! Going through the JSON configuration for every page means a lot of work and a lot of risk

Can I just write my own component to cover the layer, so THAT I can do this with minimal change by replacing the component tag

The real difference is that I changed the original EL-Table into HF-Table and supported all the functions of the original EL-Table

Ideas and Practices

El – table – the column

We can’t implement an El-Table component ourselves, so our component is just a shell on top of the El-Table, with a set button that affects the rendering of the entire table.

So since we’re not going to implement the El-Table ourselves that means we’re going to take the el-table-column in the original code, and we’re going to pass it to the El-Table, so that we can render the original table

In an instance of a component, we can use vNode to retrieve a virtual DOM, vNode to retrieve a virtual DOM, vNode to retrieve a virtual DOM, vNode to retrieve a virtual DOM, vNode to retrieve a virtual DOM, vNode to retrieve a virtual DOM, vNode to retrieve a virtual DOM, vNode to retrieve a virtual DOM. All el-table-column virtual DOM arrays can be obtained from his children

How to render a table

Now that we have all the el-table-column virtual DOM in the previous step, how do we render the virtual DOM into the corresponding table component?

This render should come on stage!!

The children is the array of the El-table-column we got. We just need to pass in the array of the virtual DOM in the form of component properties. Then create an El-table and pass the corresponding children to it!

XXX

In other words, my HF-Table is actually hijacking the El-Table. Its purpose is to take the virtual DOM originally written by El-Table-Colunm and render it into a table

The operating table

At this point, our task has been completed more than half, that is, the label of my original El-table can be replaced, then all we need to do is the operation table. What I did was simple. Now that I had all the children, I manipulated the hF-table component into the array I wanted and threw it to the render function

Component code

The code for the entire component, excluding styles, is less than 100 lines

<template>
  <div class="hf-table">

    <el-popover
      placement="bottom-end"
      width="400"
      popper-class="table-cloumn-setting-popper"
      trigger="click"
    >
      <div class="setting-row-content">
        <draggable v-model="storageList" handle=".el-icon-s-operation" @end="updateTable">
          <div v-for="clo in storageList" :key="clo.label" class="setting-row">
            <i class="el-icon-s-operation" />
            <el-checkbox v-model="clo.show" class="label" @change="showOrHidden($event,clo)">{{ clo.label }}</el-checkbox>
            <el-button
              class="btn"
              size="mini"
              :type="clo.fixed === 'left' ? 'primary' : 'default'"
              @click="setFixed('left',clo)"
            >Fixed on the left side</el-button>
            <el-button
              class="btn"
              size="mini"
              :type="clo.fixed === 'right' ? 'primary' : 'default'"
              @click="setFixed('right',clo)"
            >Fixed on the right side</el-button>
          </div>
        </draggable>
      </div>
      <i slot="reference" class="el-icon-setting" />
    </el-popover>
    <new-table v-if="showTable" :config="config" />
  </div>
</template>
<script>
import draggable from 'vuedraggable'
import newTable from './table.js'
const components = { newTable, draggable }
export default {
  components,
  props: {
    storageName: {
      type: String.default: 'hfTable'}},data() {
    return {
      showTable: false.storageList: [].name: ' '.config: {
        children: [].attrs: {},
        listeners: {}}}},watch: {
    '$attrs': {
      handler(newV) {
        this.$set(this.config, 'attrs', newV)
      },
      deep: true.immediate: true}},mounted() {
    this.initStorage()
    this.updateTable()
  },
  methods: {
    showOrHidden(val, clo) {
      if(! val &&this.storageList.filter(i= > i.show).length === 0) {
        this.$message.warning('List displays at least one column')
        this.$nextTick(() = > {
          clo.show = true
        })
        return
      }
      this.updateTable()
    },
    setFixed(value, clo) {
      if (clo.fixed === value) {
        clo.fixed = false
      } else {
        clo.fixed = value
      }
      this.updateTable()
    },
    // Initialize the cache configuration
    initStorage() {
      this.storageList = []
      const storage = window.localStorage.getItem(this.storageName)
      // If the page changes, update it to the latest node array
      let list = storage ? JSON.parse(storage) : []
      this.$vnode.componentOptions.children.forEach(node= > {
        // Use label, since text may be changed
        if(! node.componentOptions.propsData.type && list.findIndex(i= > i.label === node.componentOptions.propsData.label) < 0) {
          // This is not a special type
          const propsData = JSON.parse(JSON.stringify(node.componentOptions.propsData)) propsData.fixed = propsData.fixed ! = =undefined ? 'left' : false
          list.push({
            fixed: false.// Default additions are not fixed
            show: true.// By default new additions are displayed. propsData }) } })// The node array must exist in order to be meaningful
      list = list.filter(item= > this.$vnode.componentOptions.children.find(n= > {
        return item.label === n.componentOptions.propsData.label
      }))
      this.storageList = list
    },
    // Render the table from the cached array
    updateTable() {
      const childrenNodes = this.$vnode.componentOptions.children.filter(node= > node.componentOptions.propsData.type)
      this.storageList.forEach(item= > {
        if (item.show) {
          const node = this.$vnode.componentOptions.children.find(n= > n.componentOptions.propsData.label === item.label)
          if (node) {
            node.componentOptions.propsData.fixed = item.fixed
            childrenNodes.push(node)
          }
        }
      })
      this.config.children = childrenNodes
      this.config.attrs = this.$attrs
      this.config.listeners = this.$listeners
      this.showTable = false
      this.$nextTick(() = > {
        this.showTable = true
      })
      window.localStorage.setItem(this.storageName, JSON.stringify(this.storageList))
    }
  }
}
</script>
<style lang="scss" scoped>
  .table-cloumn-setting-popper{
    .setting-row-content{
      max-height: 600px;
      overflow-y: auto;
      .setting-row{
        height: 40px;
        line-height: 40px;
        .el-icon-s-operation{
          cursor: move;
          font-size: 16px;
          margin-right: 8px;
        }
        .label{
          margin-right: 8px;
        }
        .btn{
          padding: 4px! important; }}}}.hf-table{
    width:100%;
    height:100%;
    position: relative;
    .el-icon-setting{
      position: absolute;
      right: 20px;
      top: -20px;
      cursor: pointer; }}</style>

Copy the code

Table functional components

import Vue from 'vue'

export default Vue.component('newtable', {
  functional: true.props: {},
  listeners: {},
  render: function(h, context) {
    return h(
      'el-table',
      {
        props: context.data.attrs.config.attrs,
        on: context.data.attrs.config.listeners
      },
      context.data.attrs.config.children
    )
  }
})

Copy the code