vxe-table + ant design implements adding, deleting, modifying, moving rows up and down in tree-shaped tables.

Table of Contents

  • 1. Function Description
    • 1. Rendering
    • 2. Description
  • Two, realize
  • 3. Import

1. Function description

1. Rendering image

2. Description

  1. Added: Add table parent row
  2. Added: Add table sub-rows
  3. delete: delete the parent row
  4. More dropdown menu (move up): Move parent row/child row up
  5. More Dropdown (Move Down): Move Parent/Child Rows Down
  6. EDIT: Only the name column is editable for parent rows, there is no limit for child rows. The trigger method is to click on the cell.

2. Implementation

<template>
  <div class="box">
    <span @click="addParent" style="margin-bottom:5px">
      <a-icon type="plus-circle" style="margin-right:5px" /> New
    </span>

    <a-spin :spinning="spinning">
      <vxe-table
        border
        resizable
        auto-resize
        show-overflow
        highlight-hover-row
        highlight-current-row
        ref="xTable"
        row-id="id"
        max-height="500px"
        :data="tableData"
        :edit-config="{<!-- -->
        trigger: 'click',
        mode: 'cell',
        activeMethod: activeMethod
        }"
        :tree-config="{ children: 'children' }"
      >
        <vxe-column field="name" title="name" :edit-render="{}" tree-node>
          <template #edit="{ row }">
            <vxe-input v-model="row.name" type="text"></vxe-input>
          </template>
        </vxe-column>
        <vxe-column field="code" title="code" :edit-render="{}">
          <template #edit="{ row }">
            <vxe-input v-model="row.code" type="text"></vxe-input>
          </template>
        </vxe-column>
        <vxe-column field="condition" title="condition" :edit-render="{}">
          <template #edit="{ row }">
            <vxe-input v-model="row.condition" type="text"></vxe-input>
          </template>
        </vxe-column>
        <vxe-column title="Operation" fixed="right">
          <template #default="{ row,rowIndex }">
            <!-- parent row action button -->
            <span v-if="'children' in row">
              <span @click="addChild(row, rowIndex)" class="op-btn">Add</span>
              <span @click="deleteParent(row, rowIndex)" class="op-btn">Delete</span>
              <a-dropdown :trigger="['click']">
                <a @click.prevent>
                  <a-icon type="dash" style="color:black;transform: rotate(90deg);" />
                </a>
                <template #overlay>
                  <a-menu>
                    <a-menu-item :key="0" @click="upRow(row, rowIndex)">Move up</a-menu-item>
                    <a-menu-item :key="1" @click="downRow(row, rowIndex)">Move down</a-menu-item>
                  </a-menu>
                </template>
              </a-dropdown>
            </span>
            <!-- sub row operation button -->
            <span v-else>
              <span @click="deleteChild(row, rowIndex)" class="op-btn">delete</span>
              <span @click="upRow(row, rowIndex)" class="op-btn">Move Up</span>
              <span @click="downRow(row, rowIndex)" class="op-btn">Move Down</span>
            </span>
          </template>
        </vxe-column>
      </vxe-table>
    </a-spin>
  </div>
</template>

<script>

export default {<!-- -->
  data() {<!-- -->
    return {<!-- -->
      spinning: false, // table loading status
      tableData: [], // table data
      moveArray: [] // Move the array
    }
  },
  async created() {<!-- -->
    await this. getTableData()
  },
  methods: {<!-- -->
    // get table data
    async getTableData() {<!-- -->
      // 1. Turn on the form loading state
      this.spinning = true
      // 2. Get form data
      this.tableData = [
        {<!-- -->
          creator: 'Xiaohong',
          code: 'xh',
          name: 'Craft 1',
          description: '',
          condition: '',
          id: 1,
          children: [
            {<!-- -->
              creator: '红红',
              code: 'hh',
              name: 'Craft 11',
              condition: '',
              description: '',
              id: 11
            },
            {<!-- -->
              creator: 'Flower',
              code: 'hh',
              name: 'Craft 12',
              condition: '',
              description: '',
              id: 12
            },
            {<!-- -->
              creator: 'aa',
              code: 'hh',
              name: 'Craft 13',
              condition: '',
              description: '',
              id: 13
            }
          ]
        },
        {<!-- -->
          creator: 'Xiaolan',
          code: 'xl',
          name: 'Craft 2',
          description: '',
          condition: '',
          id: 2,
          children: [
            {<!-- -->
              creator: 'Blue',
              code: 'll',
              name: 'Craft 22',
              condition: '',
              description: '',
              id: 21
            }
          ]
        },
        {<!-- -->
          creator: 'Xiaobai',
          code: 'xb',
          name: 'Craft 3',
          condition: '',
          description: '',
          id: 3,
          children: []
        }
      ]
      // 3. Close the form loading state
      this. spinning = false
    },
    // Add table parent row
    addParent() {<!-- -->
      const record = {<!-- -->
        name: '',
        code: '',
        condition: '',
        id: this. getGUID()
      }
      this. tableData. push(record)
      this.$message.success('Add successfully!')
    },
    // delete parent row
    deleteParent(row, rowIndex) {<!-- -->
      this.tableData.splice(rowIndex, 1)
      this.$message.success('delete successfully!')
    },
    // delete child
    deleteChild(row) {<!-- -->
      let info = this. findChildIndex(this. tableData, row. id)
      this.tableData[info[1]].children.splice(info[0], 1)
      this.$message.success('delete successfully!')
    },
    // move down
    downRow(row, rowIndex) {<!-- -->
      // 1. The default is the data of the parent row
      let index = rowIndex
      this.moveArray = this.tableData
      // 2. If you move up the child row, you need to find the child row index and the parent row index
      if ('children' in row === false) {<!-- -->
        // Find child row, parent row index
        let info = this. findChildIndex(this. tableData, row. id)
        // update data
        index = info[0]
        this.moveArray = this.tableData[info[1]].children
      }
      // 3. is the last line
      if (index === this.moveArray.length - 1) {<!-- -->
        this.$message.warning('This line cannot be moved down!')
      } else {<!-- -->
        // 4. Not the last line
        this.moveArray = this.swapItem(this.moveArray, index, index + 1)
        this.$message.success('Move down successfully!')
      }
    },
    // move up
    upRow(row, rowIndex) {<!-- -->
      // 1. The default is the data of the parent row
      let index = rowIndex
      this.moveArray = this.tableData
      // 2. If you move up the child row, you need to find the child row index and the parent row index
      if ('children' in row === false) {<!-- -->
        // Find child row, parent row index
        let info = this. findChildIndex(this. tableData, row. id)
        // update data
        index = info[0]
        this.moveArray = this.tableData[info[1]].children
      }
      // 3. is the first row
      if (index === 0) {<!-- -->
        this.$message.warning('This line cannot be moved up!')
      } else {<!-- -->
        // 4. Not the first line
        this.moveArray = this.swapItem(this.moveArray, index, index - 1)
        this.$message.success('Move up successfully!')
      }
    },
    swapItem(arr, index1, index2) {<!-- -->
      arr[index1] = arr.splice(index2, 1, arr[index1])[0]
      return arr
    },
    // Check if the cell is editable
    activeMethod({<!-- --> row, column }) {<!-- -->
      // parent row only name editable
      let tag =
        'children' in row & amp; & amp;
        ['code', 'condition'].includes(column.property)
      return !tag
    },
    // add child
    addChild(row, rowIndex) {<!-- -->
      const record = {<!-- -->
        name: '',
        code: '',
        condition: '',
        description: '',
        id: this. getGUID()
      }
      this.tableData[rowIndex].children.push(record)
      this.$message.success('Add successfully!')
    },
    // Find the index of the child row, the index of the parent row
    findChildIndex(data, id) {<!-- -->
      let result = []
      function find(data, id, parentId) {<!-- -->
        for (let i = 0; i < data. length; i ++ ) {<!-- -->
          if (result. length > 0) {<!-- -->
            return
          }
          if (data[i].id === id) {<!-- -->
            result. push(i)
            result. push(parentId)
          }
          if (data[i].children & amp; & amp; data[i].children.length > 0) {<!-- -->
            find(data[i].children, id, i)
          }
        }
      }
      find(data, id, 0)
      return result
    },
    // randomly generate id
    getGUID() {<!-- -->
      var d = new Date().getTime()
      var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
        /[xy]/g,
        function (c) {<!-- -->
          var r = (d + Math.random() * 16) % 16 | 0
          d = Math. floor(d / 16)
          return (c == 'x' ? r : (r & amp; 0x3) | 0x8).toString(16)
        }
      )
      return uuid
    }
  }
}
</script>

<style>
.box {<!-- -->
  width: 1000px;
  margin: 100px;
}

.op-btn {<!-- -->
  margin: 0 2px;
  cursor: pointer;
}
</style>

3. Import

  • This feature uses Ant Design + vxe-table
  • For related import instructions, please refer to the article