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