Vue’s advanced table component library [vxe-table]

Article directory

  • Preface
  • vxe-table
  • Official website
  • Implement table header drag and drop
  • tree table
  • Full keyboard operation
  • Afterword

Foreword

hello world welcome to the new world of front-end

Current article series column: Front-end series of articles
?Bloggers still have a lot of knowledge and technology to master in the front-end field, and are constantly working hard to fill technical shortcomings. (If there is an error, thank you for pointing it out)
Thank you for your support! Your viewing is the motivation for the author’s creation

vxe-table

A UI component library specially used to perform complex operations on Vue3 tables, including virtual scrolling, row dragging of ordinary tables, full-screen display of tables, full keyboard operations of tables, disabled editing, etc.

Official website

Official website address

download

npm install xe-utils vxe-table

Configured in main.js or main.ts

import {<!-- --> App, createApp } from 'vue'
import VXETable from 'vxe-table'
import 'vxe-table/lib/style.css'

function useTable (app: App) {<!-- -->
app.use(VXETable)
}

createApp(App).use(useTable).mount('#app')

Realize table header dragging

Copy directly

<template>
  <div>
    <vxe-grid v-bind="gridOptions">
      <template #toolbar_buttons>
        <vxe-button @click="gridOptions.align = 'left'">Left</vxe-button>
        <vxe-button @click="gridOptions.align = 'center'">Center</vxe-button>
        <vxe-button @click="gridOptions.align = 'right'">right</vxe-button>
      </template>
    </vxe-grid>
  </div>
</template>

<script lang="ts" setup>
import {<!-- --> reactive } from 'vue'
import {<!-- --> VxeGridProps } from 'vxe-table'

interface RowVO {<!-- -->
  id: number
  name: string
  nickname: string
  role: string
  sex: string
  age: number
  address: string
}

const gridOptions = reactive<VxeGridProps<RowVO>>({<!-- -->
  border: true,
  height: 300,
  align: null,
  columnConfig: {<!-- -->
    resizable: true
  },
  columns: [
    {<!-- --> type: 'seq', width: 50 },
    {<!-- --> field: 'name', title: 'name' },
    {<!-- --> field: 'sex', title: 'sex' },
    {<!-- --> field: 'address', title: 'Address' }
  ],
  toolbarConfig: {<!-- -->
    slots: {<!-- -->
      buttons: 'toolbar_buttons'
    }
  },
  data: [
    {<!-- --> id: 10001, name: 'Test1', nickname: 'T1', role: 'Develop', sex: 'Man', age: 28, address: 'Shenzhen' },
    {<!-- --> id: 10002, name: 'Test2', nickname: 'T2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' },
    {<!-- --> id: 10003, name: 'Test3', nickname: 'T3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' },
    {<!-- --> id: 10004, name: 'Test4', nickname: 'T4', role: 'Designer', sex: 'Women', age: 23, address: 'Shenzhen' },
    {<!-- --> id: 10005, name: 'Test5', nickname: 'T5', role: 'Develop', sex: 'Women', age: 30, address: 'Shanghai' },
    {<!-- --> id: 10006, name: 'Test6', nickname: 'T6', role: 'Designer', sex: 'Women', age: 21, address: 'Shenzhen' },
    {<!-- --> id: 10007, name: 'Test7', nickname: 'T7', role: 'Test', sex: 'Man', age: 29, address: 'Shenzhen' },
    {<!-- --> id: 10008, name: 'Test8', nickname: 'T8', role: 'Develop', sex: 'Man', age: 35, address: 'Shenzhen' }
  ]
})
</script>

Tree table

<template>
  <div>
    <vxe-toolbar>
      <template #buttons>
        <vxe-button @click="expandAllEvent">Expand all</vxe-button>
        <vxe-button @click="claseExpandEvent">Collapse all</vxe-button>
      </template>
    </vxe-toolbar>

    <vxe-table
      show-overflow
      height="400"
      ref="tableRef"
      :loading="loading"
      :tree-config="{transform: true}"
      :scroll-y="{enabled: true, gt: 20}"
      :data="tableData">
      <vxe-column type="seq" width="200" tree-node></vxe-column>
      <vxe-column field="id" title="Id"></vxe-column>
      <vxe-column field="name" title="Name"></vxe-column>
    </vxe-table>
  </div>
</template>

<script lang="ts" setup>
import {<!-- --> ref } from 'vue'
import {<!-- --> VxeTableInstance } from 'vxe-table'

interface RowVO {<!-- -->
  id: number
  parentId: number | null
  name: string
}

const tableRef = ref<VxeTableInstance<RowVO>>()

const loading = ref(false)
const tableData = ref<RowVO[]>([])

const loadList = () => {<!-- -->
  loading.value = true
  fetch('/resource/json/provinces_list.json').then(res => res.json()).then((data: RowVO[]) => {<!-- -->
    tableData.value = data
    loading.value = false
  })
}

const expandAllEvent = () => {<!-- -->
  const $table = tableRef.value
  if ($table) {<!-- -->
    $table.setAllTreeExpand(true)
  }
}

const claseExpandEvent = () => {<!-- -->
  const $table = tableRef.value
  if ($table) {<!-- -->
    $table.clearTreeExpand()
  }
}

loadList()
</script>

Full keyboard operation

template

<vxe-toolbar :refresh="{query: findList}">
          <template #buttons>
            <vxe-button>
              <template #default>New operation</template>
              <template #dropdowns>
                <vxe-button type="text" @click="insertEvent(null)">Insert from the first row</vxe-button>
                <vxe-button type="text" @click="insertEvent(-1)">Insert from the end</vxe-button>
                <vxe-button type="text" @click="insertEvent($refs.xTable.getData(100))">Insert to row 100</vxe-button>
                <vxe-button type="text" @click="insertEvent($refs.xTable.getData(400))">Insert to row 400</vxe-button>
              </template>
            </vxe-button>
            <vxe-button>
              <template #default>Delete operation</template>
              <template #dropdowns>
                <vxe-button type="text" @click="$refs.xTable.removeCheckboxRow()">Remove check</vxe-button>
                <vxe-button type="text" @click="$refs.xTable.remove($refs.xTable.getData(0))">Remove the first row</vxe-button>
                <vxe-button type="text" @click="$refs.xTable.remove($refs.xTable.getData($refs.xTable.getData().length - 1))">Remove the last row</vxe- button>
                <vxe-button type="text" @click="$refs.xTable.remove($refs.xTable.getData(100))">Delete row 100</vxe-button>
              </template>
            </vxe-button>
            <vxe-button>
              <template #default>Verification operation</template>
              <template #dropdowns>
                <vxe-button type="text" @click="validEvent">Quick verification</vxe-button>
                <vxe-button type="text" @click="fullValidEvent">Full quick verification</vxe-button>
                <vxe-button type="text" @click="selectValidEvent">Selected row verification</vxe-button>
              </template>
            </vxe-button>
            <vxe-button @click="getInsertEvent">Get new</vxe-button>
            <vxe-button @click="getRemoveEvent">Get Removed</vxe-button>
            <vxe-button @click="getUpdateEvent">Get modifications</vxe-button>
            <vxe-button>
              <template #default>Scroll operation</template>
              <template #dropdowns>
                <vxe-button type="text" @click="$refs.xTable.scrollToRow($refs.xTable.getData(10))">Scroll to row 10</vxe-button>
                <vxe-button type="text" @click="$refs.xTable.scrollToRow($refs.xTable.getData(400))">Scroll row 400</vxe-button>
                <vxe-button type="text" @click="$refs.xTable.scrollToColumn($refs.xTable.getColumns(1))">Scroll column 1</vxe-button>
                <vxe-button type="text" @click="$refs.xTable.scrollToColumn($refs.xTable.getColumns(10))">Scroll column 10</vxe-button>
              </template>
            </vxe-button>
          </template>
        </vxe-toolbar>

        <vxe-table
          border
          show-overflow
          keep-source
          ref="xTable"
          height="300"
          :column-config="{resizable: true}"
          :loading="demo1.loading"
          :edit-rules="demo1.validRules"
          :mouse-config="{selected: true}"
          :edit-config="{trigger: 'dblclick', mode: 'cell', showStatus: true}"
          :keyboard-config="{isArrow: true, isDel: true, isEnter: true, isTab: true, isEdit: true}">
          <vxe-column type="checkbox" width="60"></vxe-column>
          <vxe-column type="seq" width="100"></vxe-column>
          <vxe-column field="name" title="Name" sortable width="200" :edit-render="{autofocus: '.vxe-input--inner'}">
            <template #edit="scope">
              <vxe-input v-model="scope.row.name" type="text" @change="$refs.xTable.updateStatus(scope)"></vxe-input>
            </template>
          </vxe-column>
          <vxe-column field="age" title="Age" width="200" :edit-render="{autofocus: '.vxe-input--inner'}">
            <template #edit="scope">
              <vxe-input v-model="scope.row.age" type="text" @change="$refs.xTable.updateStatus(scope)"></vxe-input>
            </template>
          </vxe-column>
          <vxe-column field="sex" title="Sex" width="200" :edit-render="{autofocus: '.vxe-input--inner'}">
            <template #edit="scope">
              <vxe-input v-model="scope.row.sex" type="text" @change="$refs.xTable.updateStatus(scope)"></vxe-input>
            </template>
          </vxe-column>
          <vxe-column field="rate" title="Rate" width="200"></vxe-column>
          <vxe-column field="region" title="Region" width="200"></vxe-column>
          <vxe-column field="time" title="Time" width="200"></vxe-column>
          <vxe-column field="address" title="Address" width="300" show-overflow></vxe-column>
          <vxe-column field="updateTime" title="UpdateTime" width="200"></vxe-column>
          <vxe-column field="createTime" title="CreateTime" width="200"></vxe-column>
        </vxe-table>

script

import {<!-- --> defineComponent, reactive, ref } from 'vue'
        import {<!-- --> VXETable, VxeTableInstance, VxeTablePropTypes } from 'vxe-table'

        export default defineComponent({<!-- -->
          setup () {<!-- -->
            const xTable = ref<VxeTableInstance>()

            const demo1 = reactive({<!-- -->
              loading: false,
              validRules: {<!-- -->
                name: [
                  {<!-- --> required: true, message: 'app.body.valid.rName' },
                  {<!-- --> min: 3, max: 50, message: 'Name must be between 3 and 50 characters long' }
                ],
                sex: [
                  {<!-- --> required: true, message: 'Gender must be filled in' }
                ]
              } as VxeTablePropTypes.ValidConfig
            })

            const mockList = (size: number) => {<!-- -->
              const list: any[] = []
              for (let index = 0; index < size; index ++ ) {<!-- -->
                list.push({<!-- -->
                  checked: false,
                  name: `Name${<!-- -->index}`,
                  sex: '0',
                  num: 123,
                  age: 18,
                  num2: 234,
                  rate: 3,
                  address: 'shenzhen'
                })
              }
              return list
            }

            const findList = () => {<!-- -->
              demo1.loading = true
              return new Promise(resolve => {<!-- -->
                setTimeout(() => {<!-- -->
                  const tableData = mockList(600)
                  // Block Vue from monitoring large arrays to avoid short-term lag caused by Vue binding big data.
                  const $table = xTable.value
                  if ($table) {<!-- -->
                    $table.loadData(tableData)
                  }
                  resolve(null)
                  demo1.loading = false
                }, 300)
              })
            }

            const validEvent = async () => {<!-- -->
              const $table = xTable.value
              const errMap = await $table.validate()
              if (errMap) {<!-- -->
                VXETable.modal.message({<!-- --> status: 'error', content: 'Verification failed!' })
              } else {<!-- -->
                VXETable.modal.message({<!-- --> status: 'success', content: 'Verification successful!' })
              }
            }

            const fullValidEvent = async () => {<!-- -->
              const $table = xTable.value
              const errMap = await $table.fullValidate()
              if (errMap) {<!-- -->
                const msgList: string[] = []
                Object.values(errMap).forEach((errList) => {<!-- -->
                  errList.forEach(params => {<!-- -->
                    const {<!-- --> rowIndex, column, rules } = params
                    rules.forEach(rule => {<!-- -->
                      msgList.push(`No. ${<!-- -->rowIndex + 1} row ${<!-- -->column.title} Verification error: ${<!-- -->rule.message} `)
                    })
                  })
                })
                VXETable.modal.message({<!-- -->
                  status: 'error',
                  slots: {<!-- -->
                    default () {<!-- -->
                      return [
                        <div class="red" style="max-height: 400px;overflow: auto;">
                          {<!-- -->
                            msgList.map(msg => {<!-- -->
                              return <div>{<!-- --> msg }</div>
                            })
                          }
                        </div>
                      ]
                    }
                  }
                })
              } else {<!-- -->
                VXETable.modal.message({<!-- --> status: 'success', content: 'Verification successful!' })
              }
            }

            const selectValidEvent = async () => {<!-- -->
              const $table = xTable.value
              const selectRecords = $table.getCheckboxRecords()
              if (selectRecords.length > 0) {<!-- -->
                const errMap = await $table.validate(selectRecords).catch(errMap => errMap)
                if (errMap) {<!-- -->
                  VXETable.modal.message({<!-- --> status: 'error', content: 'Verification failed!' })
                } else {<!-- -->
                  VXETable.modal.message({<!-- --> status: 'success', content: 'Verification successful!' })
                }
              } else {<!-- -->
                VXETable.modal.message({<!-- --> status: 'warning', content: 'No data selected!' })
              }
            }

            const insertEvent = (row: any) => {<!-- -->
              const $table = xTable.value
              const record = {<!-- -->
                checked: false
              }
              $table.insertAt(record, row).then(({<!-- --> row }) => {<!-- -->
                $table.setEditRow(row)
              })
            }

            const getInsertEvent = () => {<!-- -->
              const $table = xTable.value
              const insertRecords = $table.getInsertRecords()
              VXETable.modal.alert(insertRecords.length)
            }

            const getRemoveEvent = () => {<!-- -->
              const $table = xTable.value
              const removeRecords = $table.getRemoveRecords()
              VXETable.modal.alert(removeRecords.length)
            }

            const getUpdateEvent = () => {<!-- -->
              const $table = xTable.value
              const updateRecords = $table.getUpdateRecords()
              VXETable.modal.alert(updateRecords.length)
            }

            findList()

            return {<!-- -->
              xTable,
              demo1,
              findList,
              validEvent,
              fullValidEvent,
              selectValidEvent,
              insertEvent,
              getInsertEvent,
              getRemoveEvent,
              getUpdateEvent
            }
          }
        })

Afterword

Creation is not easy. If this article is of some help to readers, you may wish to support it three times. Your encouragement is the motivation for bloggers to create.