The most common thing I encountered in business development using Vue was table. The project used Vue+ Element-UI. Normal writing is as follows: I tried to write snippets every time I quickly copied and pasted them. On this basis, we have made some improvements. If there is anything wrong, I hope you can point it out. Thank you very much.

<el-table
  :data="tableData"
  style="width: 100%">
  <el-table-column
    prop="date"
    label="Date"
    width="180">
  </el-table-column>
  <el-table-column
    prop="name"
    label="Name"
    width="180">
  </el-table-column>
  <el-table-column
    prop="address"
    label="Address">
  </el-table-column>
</el-table>
Copy the code

We have used Ant-Desgin before, and we can imagine passing in two basic attributes, column and data, according to this idea of table design.
El-table-column can understand that each item is passed in the corresponding prop, label and the corresponding attribute of el-table-column through traversal, using JSX notation can be more intuitive.

Order processing

In addition, the table must be in loading state before data is obtained, so loading is required. JSX syntax is used for V-loading

const directives = {
  directives: [{ name: 'loading'.value: this.loading }]
};
Copy the code

The event processing

  • In addition, you may need to do some clicking on the line information, pass in some event methods, and the syntax in JSX
const listeners = {
  on: {['row-click'] :(row, column, event) = >
      this.$emit('row-click', row, column, event)
  }
};
Copy the code

Additional props, attrs processing

$props and $attrs in the vue component instance provide a lot of convenience for additional properties and props, which can be passed in via vm.$props and vm.$attrs

Ps: $attrs contains feature bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, it contains all parent-scoped bindings and can be passed in to the internal component with v-bind=”$attrs”.

Cells are operable

Normal business may require input boxes to be passed in for some cells

The incoming slot

  1. The first idea is to pass in the slot attribute in the column{slot: 'opt', label: 'operation ', align: 'center'}
<el-table-column
  prop="date"
  label="Date"
  width="180">
  <template #opt>
    <el-input />
  </template>
</el-table-column>
Copy the code

Slot is mainly used in higher vUE versions of v-slot. Refer to the article [Vue] slot for a detailed understanding of slot. Slot, slot-scope, and V-slot are actually compiled by the main implementation inside

slot = {
  default: [VNode],
  opt: [VNode]
}
Copy the code

The elements inserted under the component are actually added to the default key-value pairs

If template is used, it is internally compiled to

```javascript scopedSlots: _vm._u([ { key: "header", fn: The function () {return [_c (" div ", [_vm. _v (" named slot header ")])]}, proxy: $scopedSlots[slotName](props) '.$scopedSlots[slotName](props) '=' {row} 'Copy the code

Incoming components

  1. The second idea is to pass in the Component property in column
component: {
  name: 'email'.render() {
    return <div>Custom Components</div>; }}Copy the code

The Vue component is essentially an object containing the name and Render methods, which are used to make judgments when creating virtual nodes

if (isObject(Ctor)) {
  Ctor = baseCtor.extend(Ctor);
}
Copy the code

Extend to point sub to the prototype that points to the superclass, merge the superclass’s options into the subclass, and add the superclass’s methods to the subclass

Passing in the render method

  1. The third way is to pass the Render method directly to column
render(h, { row }) {
  return <div>Custom Components</div>;
}
Copy the code

Render (createElement); render (createElement); render (createElement); render (createElement); So the render method passing in the createElement and the current information you want to call back to the render column allows you to perform normal operations on external data without having to step into the Template to modify it. So you can get rid of the template completely and convert the file to a.js file by exposing the object.

Putting a child under a parent can be described as an operation on scopedSlot, so you can operate on the inside of a scopedSlot

const scopedSlots =
  column.slot || column.render || column.component
    ? {
        scopedSlots: {
          default(scope) {
            const exportVal = {
              column: scope.column,
              $index: scope.$index,
              row: scope.row
            };
            if (column.slot) {
              return self.$scopedSlots[column.slot](exportVal);
            } else if (column.render) {
              return column.render(h, exportVal);
            } else if (column.component) {
              return <column.component />; }}}} : {};Copy the code

The end result is as follows

Then you just need to introduce
in the page

Some think

For some methods to merge multi-row cells also carried out corresponding implementation, for the multi-column cell merger processing up to feel very troublesome, using the original table for implementation, later will be shared.

The final code

/* eslint-disable indent */
export default {
  name: 'customTable'.props: {
    data: {
      type: Array.default: () = >[{date: '2016-05-02'.name: 'Wang Xiaohu'.address: Lane 1518, Jinshajiang Road, Putuo District, Shanghai.email: '2222'
        },
        {
          date: '2016-05-04'.name: 'Wang Xiaohu'.address: Lane 1517, Jinshajiang Road, Putuo District, Shanghai.email: 'xxxx'
        },
        {
          date: '2016-05-06'.name: 'Wang Xiaohu'.address: Lane 1517, Jinshajiang Road, Putuo District, Shanghai.email: 'dddd'}},columns: {
      type: Array.default: () = >[{prop: 'date'.label: 'date'.align: 'center' },
        { prop: 'name'.label: 'name'.align: 'center' },
        { prop: 'address'.label: 'address'.align: 'center' },
        {
          prop: 'email'.label: 'email'.align: 'center'.component: {
            name: 'email'.props: ['column'].render() {
              return <div>Custom Components</div>; }}}Elements in the // // template must have the corresponding slot="opt" attribute
        / / {slot: 'opt1, label:' input, the align: 'center'},
        // {slot: 'opt', label: 'operation ', align: 'center'}]},loading: {
      type: Boolean.default: false
    },
    spanMethod: {
      type: Function.default: () = >{}},showSummary: {
      type: Boolean.default: false
    },
    summaryMethod: {
      type: Function.default: () = >{}}},methods: {
    getAttrs(h, column) {
      const self = this;
      const scopedSlots =
        column.slot || column.render || column.component
          ? {
              scopedSlots: {
                default(scope) {
                  const exportVal = {
                    column: scope.column,
                    $index: scope.$index,
                    row: scope.row
                  };
                  if (column.slot) {
                    return self.$scopedSlots[column.slot](exportVal);
                  } else if (column.render) {
                    return column.render(h, exportVal);
                  } else if (column.component) {
                    returnh(column.component); }}}} : {};return {
        ...scopedSlots,
        attrs: {
          ...column,
          'show-overflow-tooltip': true.align: column.align || 'center'}}; }},render(h) {
    const directives = {
      directives: [{ name: 'loading'.value: this.loading }]
    };
    const listeners = {
      on: {['row-click'] :(row, column, event) = >
          this.$emit('row-click', row, column, event),
        ['selection-change'] :row= > this.$emit('selection-change', row)
      }
    };

    return (
      <el-table
        {.{ attrs: this.$attrs }}
        {.{ props: this.$props }}
        {. directives}
        {. listeners}
        class={{ 'base-table': true, 'mb-2': true }}
        ref="table"
      >
        {this.columns.map(column => {
          return (
            <el-table-column key={column.prop} {. this.getAttrs(h.column)} >
              {column.children &&
                column.children.map(c => {
                  return (
                    <el-table-column key={c.prop} {. this.getAttrs(h.c)} / >
                  );
                })}
            </el-table-column>
          );
        })}
      </el-table>); }};Copy the code