Skip to content

Table ​

Generate tables through configuration, with built-in features more suitable for middle and back-end business.

TIP

Some attributes are configured by default inside z-table, for example: align is set to center

Basic Usage ​

Configure column to generate table items. el-table related attributes are passed directly on the z-table component, el-table-column attributes are configured in column.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref<RowData[]>([])
const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
})

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.value.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :columns="columns"
    :loading="loading"
    @refresh="getTableData"
  >
    <template #expand="row">
      {{ row.$index }}
    </template>
  </z-table>
</template>

Table Title ​

Configure title attribute to generate table title, supports string and function types, can also use tableTitle slot for customization.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref<RowData[]>([])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
})

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.value.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :columns="columns"
    :loading="loading"
    title="Table Title"
    @refresh="getTableData"
  />
</template>

Operation Buttons ​

Configure operation items in column, pass button to type, configure buttons array. Button attributes can be configured directly in operation items.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import type { TableColumnScopeData } from 'ideaz-element'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref<RowData[]>([])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
})

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
  {
    type: 'button',
    label: 'Actions',
    buttons: [
      {
        type: 'primary',
        link: true,
        label: 'Edit',
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'edit')
        },
      },
      {
        type: 'danger',
        label: 'Delete',
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'delete')
        },
      },
    ],
  },
])

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.value.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :columns="columns"
    :loading="loading"
    @refresh="getTableData"
  />
</template>

Operation buttons also support dynamic attributes, such as: disabled, etc. Pass a method with current row related data as parameter.

<!-- eslint-disable unused-imports/no-unused-vars -->
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import type { TableColumnScopeData } from 'ideaz-element'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref<RowData[]>([])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
})

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
  {
    type: 'button',
    label: 'Actions',
    buttons: [
      {
        type: 'primary',
        link: true,
        label: 'Edit',
        disabled: ({ row, column, $index }: TableColumnScopeData<RowData>) => row.name === 'Steven',
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'edit')
        },
      },
      {
        type: 'danger',
        link: true,
        label: 'Delete',
        disabled: ({ row, column, $index }: TableColumnScopeData<RowData>) => row.age === 18,
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'delete')
        },
      },
    ],
  },
])

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.value.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :columns="columns"
    :loading="loading"
    @refresh="getTableData"
  />
</template>

Operation Dropdown ​

If dropdown is needed, configure type as dropdown in buttons array, configure dropdown options in children

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import type { TableColumnScopeData } from 'ideaz-element'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
  {
    type: 'button',
    label: 'Actions',
    width: '200px',
    buttons: [
      {
        type: 'primary',
        link: true,
        label: 'Edit',
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'edit')
        },
      },
      {
        type: 'danger',
        link: true,
        label: 'Delete',
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'delete')
        },
      },
      {
        type: 'dropdown',
        reference: 'Delete',
        children: [
          {
            type: 'primary',
            link: true,
            label: 'Copy',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'copy')
            },
          },
          {
            type: 'danger',
            link: true,
            label: 'Actions',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'operate')
            },
          },
        ],
      },
    ],
  },
])
</script>

<template>
  <z-table
    :data="tableData"
    :loading="loading"
    :columns="columns"
  />
</template>

reference field configures dropdown reference text (default is More), supports function and string types.

Supports configuring el-dropdown and el-dropdown-item component attributes and methods.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import type { TableColumnScopeData } from 'ideaz-element'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
  {
    type: 'button',
    label: 'Actions',
    buttons: [
      {
        type: 'dropdown',
        reference: 'Actions',
        placement: 'top-start',
        children: [
          {
            type: 'primary',
            link: true,
            label: 'Edit',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'edit')
            },
          },
          {
            type: 'danger',
            link: true,
            label: 'Delete',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'delete')
            },
          },
        ],
      },
      {
        type: 'dropdown',
        reference: () => h('span', { style: { cursor: 'pointer' } }, 'Actions2'),
        placement: 'top',
        onVisibleChange: (visible: boolean) => {
          console.log(visible, 'visible')
        },
        children: [
          {
            type: 'primary',
            link: true,
            label: 'Copy',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'copy')
            },
          },
          {
            type: 'danger',
            link: true,
            label: 'Actions',
            divided: true,
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'operate')
            },
          },
        ],
      },
    ],
  },
])
</script>

<template>
  <z-table
    :data="tableData"
    :loading="loading"
    :columns="columns"
  />
</template>

Pagination ​

Configure pagination, supports two-way binding to implement pagination effect. layout defaults to total, sizes, prev, pager, next, jumper, pageSizes defaults to [100, 200, 300, 400, 500], supports customization.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref<RowData[]>([])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
  layout: 'total, prev, pager, next',
})

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.value.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :columns="columns"
    :loading="loading"
    @refresh="getTableData"
  />
</template>

Configure paginationLeft, paginationRight, paginationTop, paginationBottom slots to customize pagination top, bottom, left, right content.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import type { Pagination } from 'ideaz-element'

const loading = ref(false)
const tableData = ref<any>([])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
  layout: 'total, sizes, prev, pager, next, jumper',
})

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

function mockApi(pagination: Pagination): Promise<{ result: { page: number, pageSize: number, total: number, list: any[] } }> {
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-table
    v-model:pagination="pagination"
    :data="tableData"
    :loading="loading"
    :columns="columns"
    @refresh="getTableData"
  >
    <template #paginationTop>
      <div class="mb-4">
        <el-card>paginationTop</el-card>
      </div>
    </template>
    <template #paginationBottom>
      <div class="mb-4">
        <el-card>paginationBottom</el-card>
      </div>
    </template>
    <template #paginationLeft>
      <div class="mb-4">
        <el-card>paginationLeft</el-card>
      </div>
    </template>
    <template #paginationRight>
      <div class="mb-4">
        <el-card>paginationRight</el-card>
      </div>
    </template>
  </z-table>
</template>

Frontend Pagination ​

Configure pagination type as front to enable frontend pagination functionality, pass all data to totalData field.

When pageSize is 0, pagination is false or pagination is not passed, pagination is not displayed.

[]
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref([])
const totalData = ref<RowData[]>([])
const pagination = ref({
  type: 'front',
  page: 1,
  pageSize: 2,
  total: 0,
  layout: 'total, sizes, prev, pager, next, jumper',
})

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'pagination front params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const data = [
        {
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
        {
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]

      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: data,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    totalData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  {{ tableData }}
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :loading="loading"
    :columns="columns"
    :total-data="totalData"
    @refresh="getTableData"
  />
</template>

Pagination Position ​

Configure pagination align field, supports left, center, right, defaults to right.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref<RowData[]>([])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
  layout: 'total, sizes, prev, pager, next, jumper',
  align: 'center',
})
const alignOptions = [
  { label: 'left', value: 'left' },
  { label: 'center', value: 'center' },
  { label: 'right', value: 'right' },
]

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.value.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-radio v-model="pagination.align" :options="alignOptions" type="radio-button" />
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :columns="columns"
    :loading="loading"
    @refresh="getTableData"
  />
</template>

Hide Columns ​

Configure hide field in column, supports function or boolean value. Function returns boolean value, true means hide, false means show.

<script lang="ts" setup>
import { ref } from 'vue'

const isHide = ref(false)
const tableData = ref([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
    hide: () => isHide.value,
  },
  {
    prop: 'gender',
    label: 'Gender',
    hide: true,
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

function changeVisible() {
  isHide.value = !isHide.value
}
</script>

<template>
  <el-button @click="changeVisible">
    Toggle column visibility
  </el-button>
  <z-table
    :data="tableData"
    :columns="columns"
  />
</template>

Custom Column ​

Configure slot or render in column to implement custom column content.

<script lang="ts" setup>
import { ref } from 'vue'
import type { TableColumnScopeData } from 'ideaz-element'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const tableData = ref<RowData[]>([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
    render: ({ row }: TableColumnScopeData<RowData>) => h('span', row.name),
  },
  {
    prop: 'gender',
    label: 'Gender',
    slot: 'gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
  >
    <template #gender="{ row }">
      <el-input :model-value="row.gender" />
    </template>
  </z-table>
</template>

Column Types ​

Configure type in column to implement table column types, supports expand, radio, selection, index.

Supports custom column components, configure component field, supports input, select, datepicker, switch, any locally or globally registered components.

TIP

When type is radio or cross-page selection checkbox is needed, it needs to be used with rowKey (default id).

TIP

When passing components directly to component, please use markRaw wrapper to prevent performance impact.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { markRaw, ref } from 'vue'
import { ElInput } from 'element-plus'
import type { TableColumnScopeData } from 'ideaz-element'

interface RowData {
  id: number
  name: string
  gender: string
  age: number
  time: string
}

const tableData = ref<RowData[]>([
  {
    id: 1,
    name: 'Steven',
    gender: '1',
    age: 22,
    time: '2020-01-01',
  },
  {
    id: 2,
    name: 'Helen',
    gender: '1',
    age: 12,
    time: '2012-01-01',
  },
  {
    id: 3,
    name: 'Nancy',
    gender: '2',
    age: 18,
    time: '2018-01-01',
  },
  {
    id: 4,
    name: 'Jack',
    gender: '1',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    type: 'expand',
  },
  {
    type: 'index',
  },
  {
    type: 'radio',
  },
  {
    type: 'selection',
  },
  {
    component: 'input',
    prop: 'name',
    label: 'Name',
    onChange: ({ row, column, $index }: TableColumnScopeData<RowData>, val: string) => {
      console.log('change event', row, column, $index, val)
    },
    onInput: ({ row, column, $index }: TableColumnScopeData<RowData>, val: string) => {
      console.log('input event', row, column, $index, val)
    },
    fieldProps: {
      clearable: true,
    },
  },
  {
    component: 'select',
    prop: 'gender',
    label: 'Gender',
    fieldProps: {
      clearable: true,
    },
    onChange: ({ row, column, $index }: TableColumnScopeData<RowData>, val: string) => {
      console.log('change event', row, column, $index, val)
    },
  },
  {
    prop: 'age',
    label: 'Age',
    component: markRaw(ElInput),
    fieldProps: {
      clearable: true,
    },
    onChange: ({ row, column, $index }: TableColumnScopeData<RowData>, val: string) => {
      console.log('change event', row, column, $index, val)
    },
    onInput: ({ row }: TableColumnScopeData<RowData>, val: string) => {
      console.log('input event', row, val)
    },
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}
</script>

<template>
  <z-table v-model:data="tableData" :columns="columns" :options="options">
    <template #expand>
      <span>Expanded content</span>
    </template>
  </z-table>
</template>

Dynamic Attributes ​

column custom content supports dynamic attributes. Component attributes like disabled, placeholder support passing functions (function attributes are not supported yet, such as: formatter attribute of ElInput component), parameter is current row scope data.

<!-- eslint-disable unused-imports/no-unused-vars -->
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import type { TableColumnScopeData } from 'ideaz-element'

interface RowData {
  id: number
  name: string
  gender: string
  age: number
  time: string
}

const tableData = ref([
  {
    id: 1,
    name: 'Steven',
    gender: '1',
    age: 22,
    time: '2020-01-01',
  },
  {
    id: 2,
    name: 'Helen',
    gender: '1',
    age: 12,
    time: '2012-01-01',
  },
  {
    id: 3,
    name: 'Nancy',
    gender: '2',
    age: 18,
    time: '2018-01-01',
  },
  {
    id: 4,
    name: 'Jack',
    gender: '1',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    component: 'input',
    prop: 'name',
    label: 'Name',
    onChange: ({ row }: TableColumnScopeData<RowData>, val: string) => {
      console.log('change event', row, val)
    },
    onInput: ({ row }: TableColumnScopeData<RowData>, val: string) => {
      console.log('input event', row, val)
    },
    fieldProps: {
      clearable: true,
      disabled: ({ row, column, $index }: TableColumnScopeData<RowData>) => {
        return row.id === 1
      },
    },
  },
  {
    component: 'select',
    prop: 'gender',
    label: 'Gender',
    onChange: ({ row }: TableColumnScopeData<RowData>, val: string) => {
      console.log('change event', row, val)
    },
    fieldProps: {
      placeholder: ({ row, column, $index }: TableColumnScopeData<RowData>) => {
        return `${row.name}Gender${$index}`
      },
      clearable: true,
    },
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}
</script>

<template>
  <z-table v-model:data="tableData" :columns="columns" :options="options" />
</template>

Custom Table Header ​

Configure label in column as string with slot or Slot or configure as render function to implement custom column header.

<script lang="ts" setup>
import { h, ref } from 'vue'

const tableData = ref([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: () => h('span', 'Custom Header'),
  },
  {
    prop: 'gender',
    label: 'genderHeaderSlot',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
  >
    <template #genderHeaderSlot="scope">
      <span>Gender custom header {{ scope.$index }}</span>
    </template>
  </z-table>
</template>

Column Tooltip ​

Configure tooltip in column to implement table header tooltip functionality, supports function and string.

<script lang="ts" setup>
import { h, ref } from 'vue'

const tableData = ref([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
    tooltip: () => h('span', 'NameHint'),
  },
  {
    prop: 'gender',
    label: 'Gender',
    tooltip: 'GenderHint',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
  />
</template>

Merge Same Items ​

Configure table attribute mergeCells, supports separate configuration of rows or columns, supports field configuration.

ts
interface mergeCells {
  direction: 'row' | 'column' | 'both'  // Merge direction: row, column, or both
  props?: string[]  // Fields to merge, if not passed, all columns participate in merging
}
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  id: number
  name: string
  gender: string
  age: number
  time: string
}

const tableData = ref<RowData[]>([
  {
    id: 1,
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    id: 2,
    name: 'Steven',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    id: 3,
    name: 'female',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    id: 4,
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

</script>

<template>
  <z-table
    v-model:data="tableData"
    :columns="columns"
    :draggable="true"
    :merge-cells="{
      direction: 'both',
    }"
    border
  />
</template>

Toolbar ​

toolBar configuration item, used to configure table toolbar.

When toolBar value is false, toolbar is not displayed.

If you want to configure certain features not to display, configure the four fields refresh, density, fullScreen, setting under toolBar as false.

<script lang="ts" setup>
import { ref } from 'vue'

const tableData = ref([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
    :tool-bar="false"
  />
</template>

toolBar supports configuring default unchecked items, configure uncheck field, value is label of column items.

<script lang="ts" setup>
import { ref } from 'vue'

const tableData = ref([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
    :tool-bar="{ uncheck: ['Gender'], density: false, setting: false }"
  />
</template>

toolBar supports excluding certain table items, configure exclude field, value is label of column items.

<script lang="ts" setup>
import { ref } from 'vue'

const tableData = ref([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
    :tool-bar="{ exclude: ['Gender'], fullScreen: false, refresh: false }"
  />
</template>

toolBar top, bottom, left, right content supports customization through toolBarTop, toolBarBottom, toolBarRight, toolBarLeft, tableTitle slot configuration.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref<RowData[]>([])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
  layout: 'total, prev, pager, next',
})

const columns = ref([
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.value.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :columns="columns"
    :loading="loading"
    @refresh="getTableData"
  >
    <template #toolBarTop>
      <div class="slot-demo">
        toolBarTop
      </div>
    </template>
    <template #toolBarBottom>
      <div class="slot-demo">
        toolBarBottom
      </div>
    </template>
    <template #toolBarRight>
      <div class="slot-demo">
        toolBarRight
      </div>
    </template>
    <template #toolBarLeft>
      <div class="slot-demo">
        toolBarLeft
      </div>
    </template>
    <template #tableTitle>
      <div class="slot-demo">
        tableTitle
      </div>
    </template>
  </z-table>
</template>

<style lang="scss">
.slot-demo {
  padding: 10px;
  border: 1px solid var(--el-card-border-color);
  border-radius: var(--el-card-border-radius);
  box-shadow: var(--el-box-shadow-light);
}
</style>

Data Dragging ​

Set draggable to true to enable data dragging.

WARNING

Must configure row-key, otherwise update issues will occur.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  id: number
  name: string
  gender: string
  age: number
  time: string
}

const tableData = ref<RowData[]>([
  {
    id: 1,
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    id: 2,
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    id: 3,
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    id: 4,
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    type: 'sort',
    label: 'Sort',
  },
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])
function handleClick() {
  console.log(tableData.value, 'handleClick')
}

function handleSortEnd(data: RowData[]) {
  console.log(data, 'data')
}
</script>

<template>
  <el-button @click="handleClick">
    click
  </el-button>
  <z-table
    v-model:data="tableData"
    :columns="columns"
    :draggable="true"
    row-key="id"
    @drag-sort-end="handleSortEnd"
  />
</template>

Supports setting slot or render to customize drag icon.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const tableData = ref<RowData[]>([
  {
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    type: 'sort',
    label: 'Sort',
  },
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

function handleSortEnd(data: RowData[]) {
  console.log(data, 'data')
}
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
    :draggable="true"
    @drag-sort-end="handleSortEnd"
  >
    <template #sort>
      <el-icon size="16" class="cursor-pointer">
        <i-setting />
      </el-icon>
    </template>
  </z-table>
</template>

Editable Table ​

Set editable to true to enable table edit mode. This field supports boolean or object type, editable table type defaults to single.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

const tableData = ref([
  {
    name: 'Steven',
    gender: '1',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: '1',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: '2',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: '1',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    component: 'input',
    prop: 'name',
    label: 'Name',
  },
  {
    component: 'select',
    prop: 'gender',
    label: 'Gender',
  },
  {
    component: 'input',
    prop: 'age',
    label: 'Age',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: 'Date',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
])

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

function handleClick() {
  console.log(tableData.value, 'handleClick')
}
</script>

<template>
  <el-button @click="handleClick">
    click
  </el-button>
  <z-table
    v-model:data="tableData"
    :columns="columns"
    :options="options"
    :editable="true"
  />
</template>

Set editable type to multiple to enable multi-row edit mode.

Configure maxLength of editable to set maximum add quantity. Configure deleteConfirm of editable to true to enable delete confirmation.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

const tableData = ref([
  {
    name: 'Steven',
    gender: '1',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: '1',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: '2',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: '1',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    component: 'input',
    prop: 'name',
    label: 'Name',
  },
  {
    component: 'select',
    prop: 'gender',
    label: 'Gender',
  },
  {
    component: 'input',
    prop: 'age',
    label: 'Age',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: 'Date',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
])

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

function handleClick() {
  console.log(tableData.value, 'handleClick')
}
</script>

<template>
  <el-button @click="handleClick">
    click
  </el-button>
  <z-table
    v-model:data="tableData"
    :columns="columns"
    :options="options"
    :editable="{ type: 'multiple', deleteConfirm: true }"
  />
</template>

Configure onSave, onDelete, onEdit, onCancel of editable.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import type { EditableTableEventParams } from 'ideaz-element'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
  __isEdit?: boolean
}

const tableData = ref<RowData[]>([
  {
    name: 'Steven',
    gender: '1',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: '1',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: '2',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: '1',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    component: 'input',
    prop: 'name',
    label: 'Name',
  },
  {
    component: 'select',
    prop: 'gender',
    label: 'Gender',
  },
  {
    component: 'input',
    prop: 'age',
    label: 'Age',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: 'Date',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
])

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const editable = {
  type: 'single',
  maxLength: 5,
  onCancel: ({ row, $index, column, formRef }: EditableTableEventParams<RowData>) => {
    console.log(row, $index, column, formRef, 'row onCancel')
  },
  onSave: ({ row, $index, column, formRef }: EditableTableEventParams<RowData>) => {
    console.log(row, $index, column, formRef, 'row onSave')
  },
  onDelete: ({ row, $index, column, formRef }: EditableTableEventParams<RowData>) => {
    console.log(row, $index, column, formRef, 'row onDelete')
  },
  onEdit: ({ row, $index, column, formRef }: EditableTableEventParams<RowData>) => {
    row.__isEdit = true
    console.log(row, $index, column, formRef, 'row onEdit')
  },
}
</script>

<template>
  <z-table
    v-model:data="tableData"
    :columns="columns"
    :options="options"
    :editable="editable"
  />
</template>

Supports custom operation buttons.

<script lang="ts" setup>
import type { Ref } from 'vue'
import { ref } from 'vue'
import type { DefaultButtonOperation, TableColumnScopeData } from 'ideaz-element'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
  __isEdit: boolean
}

const tableData = ref([
  {
    name: 'Steven',
    gender: '1',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: '1',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: '2',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: '1',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    component: 'input',
    prop: 'name',
    label: 'Name',
  },
  {
    component: 'select',
    prop: 'gender',
    label: 'Gender',
  },
  {
    component: 'input',
    prop: 'age',
    label: 'Age',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: 'Date',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
  {
    type: 'button',
    label: 'Actions',
    buttons: ({ renderEdit, renderCancel, renderDelete, renderSave }: DefaultButtonOperation, tableData: Ref<RowData[]>) => {
      return [
        {
          type: 'primary',
          link: true,
          label: 'Copy',
          hide: ({ row }: TableColumnScopeData<RowData>) => row.__isEdit,
          onClick: ({ row }: TableColumnScopeData<RowData>) => {
            tableData.value.push({ ...row })
          },
        },
        renderEdit,
        renderCancel,
        renderDelete,
        renderSave,
      ]
    },
  },
])

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const editable = {
  type: 'single',
  maxLength: 5,
}
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
    :options="options"
    :editable="editable"
  />
</template>

Sticky ​

Implement sticky functionality by configuring sticky attribute's top, parent (DOM element where scroll bar appears) and zIndex. top defaults to 50px, zIndex defaults to 100.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref<RowData[]>([])
const columns = ref([
  {
    prop: 'id',
    label: 'id',
  },
  {
    prop: 'name',
    label: 'Name',
    width: 300,
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
    width: 300,
  },
  {
    prop: 'time',
    label: () => h('span', {}, 'customLabel'),
    width: 300,
  },
])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
})

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          id: 1,
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          id: 2,
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
        {
          id: 3,
          name: 'Mike',
          gender: 'male',
          age: 23,
          time: '2020-01-01',
        },
        {
          id: 4,
          name: 'Jack',
          gender: 'female',
          age: 16,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          id: 5,
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          id: 6,
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.value.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :columns="columns"
    :loading="loading"
    :sticky="{ parent: 'document' }"
    @refresh="getTableData"
  >
    <template #expand="row">
      {{ row.$index }}
    </template>
  </z-table>
</template>

Supports customizing sticky table header style through sticky.style.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'

interface RowData {
  name: string
  gender: string
  age: number
  time: string
}

const loading = ref(false)
const tableData = ref<RowData[]>([])
const columns = ref([
  {
    prop: 'id',
    label: 'id',
  },
  {
    prop: 'name',
    label: 'Name',
    width: 300,
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
    width: 300,
  },
  {
    prop: 'time',
    label: () => h('span', {}, 'customLabel'),
    width: 300,
  },
])
const pagination = ref({
  page: 1,
  pageSize: 2,
  total: 0,
})

function mockApi(params: { page: number, pageSize: number }): Promise<{ result: { page: number, pageSize: number, total: number, list: RowData[] } }> {
  console.log(params, 'params')
  return new Promise((resolve) => {
    setTimeout(() => {
      const dataFirstPage = [
        {
          id: 1,
          name: 'Steven',
          gender: 'male',
          age: 22,
          time: '2020-01-01',
        },
        {
          id: 2,
          name: 'Helen',
          gender: 'male',
          age: 12,
          time: '2012-01-01',
        },
        {
          id: 3,
          name: 'Mike',
          gender: 'male',
          age: 23,
          time: '2020-01-01',
        },
        {
          id: 4,
          name: 'Jack',
          gender: 'female',
          age: 16,
          time: '2012-01-01',
        },
      ]
      const dataSecondPage = [
        {
          id: 5,
          name: 'Nancy',
          gender: 'female',
          age: 18,
          time: '2018-01-01',
        },
        {
          id: 6,
          name: 'Jack',
          gender: 'male',
          age: 28,
          time: '2028-01-01',
        },
      ]
      resolve({
        result: {
          page: 1,
          pageSize: 10,
          total: 4,
          list: pagination.value.page === 1 ? dataFirstPage : dataSecondPage,
        },
      })
    }, 100)
  })
}

async function getTableData() {
  loading.value = true
  try {
    const res = await mockApi({ ...pagination.value })
    tableData.value = res.result.list
    pagination.value.total = res.result.total
  }
  catch (error) {
    console.log(error)
  }
  loading.value = false
}

getTableData()
</script>

<template>
  <z-table
    v-model:pagination="pagination"
    v-model:data="tableData"
    :columns="columns"
    :loading="loading"
    :sticky="{ parent: 'document', style: { border: '1px solid black' } }"
    @refresh="getTableData"
  >
    <template #expand="row">
      {{ row.$index }}
    </template>
  </z-table>
</template>

Watermark ​

Configure watermark, for details refer to el-watermark configuration.

<script lang="ts" setup>
import { ref } from 'vue'

const tableData = ref([
  {
    name: 'Steven',
    gender: '1',
    age: 22,
    time: '2020-01-01',
  },
  {
    name: 'Helen',
    gender: '1',
    age: 12,
    time: '2012-01-01',
  },
  {
    name: 'Nancy',
    gender: '2',
    age: 18,
    time: '2018-01-01',
  },
  {
    name: 'Jack',
    gender: '1',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    component: 'input',
    prop: 'name',
    label: 'Name',
  },
  {
    component: 'select',
    prop: 'gender',
    label: 'Gender',
  },
  {
    component: 'input',
    prop: 'age',
    label: 'Age',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: 'Date',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
])

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
    :options="options"
    watermark="Table watermark"
  />
</template>

Table Methods ​

z-table table methods can be used according to el-table.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  id: number
  name: string
  gender: string
  age: number
  time: string
}

const zTableRef = ref()
const radioData = ref({})
const selectionData = ref<RowData[]>([])
const tableData = ref<RowData[]>([
  {
    id: 1,
    name: 'Steven',
    gender: 'male',
    age: 22,
    time: '2020-01-01',
  },
  {
    id: 2,
    name: 'Helen',
    gender: 'male',
    age: 12,
    time: '2012-01-01',
  },
  {
    id: 3,
    name: 'Nancy',
    gender: 'female',
    age: 18,
    time: '2018-01-01',
  },
  {
    id: 4,
    name: 'Jack',
    gender: 'male',
    age: 28,
    time: '2028-01-01',
  },
])

const columns = ref([
  {
    type: 'radio',
  },
  {
    type: 'selection',
  },
  {
    prop: 'name',
    label: 'Name',
  },
  {
    prop: 'gender',
    label: 'Gender',
  },
  {
    prop: 'age',
    label: 'Age',
  },
  {
    prop: 'time',
    label: 'Date',
  },
])

function handleRadioChange(row: RowData) {
  radioData.value = row
  console.log(row, 'radio data')
}

function handleSelectionChange(data: RowData[]) {
  selectionData.value = data
  console.log(data, 'selection data')
}

function handleClear() {
  zTableRef.value.clearSelection()
}
</script>

<template>
  <el-button @click="handleClear">
    Clear selection
  </el-button>
  <z-table
    ref="zTableRef"
    :data="tableData"
    :columns="columns"
    @radio-change="handleRadioChange"
    @selection-change="handleSelectionChange"
  />
</template>

z-table Attributes ​

AttributeDescriptionTypeAccepted ValuesDefault
modelValue:dataDisplay data, supports two-way bindingarray——
modelValue:paginationPagination configuration, supports two-way bindingobject——
loadingTable loadingboolean——
titleTable titlestring / function——
columnsTable configuration itemsarray——
toolBarToolbar configurationobject / boolean——
editableEditable table configurationobject / boolean——
optionsTable internal option data sourceobject——
watermarkWatermark configurationobject (see el-watermark documentation for details)——
stickyTable header sticky configurationobject——
totalDataAll table data (effective for frontend pagination)array——
mergeCellsTable same item merge configurationobject——
heightTable height, defaults to auto height. If height is number type, unit is px; if height is string type, this height will be set as Table's style.height value, Table height will be controlled by external styles.string / number——
max-heightTable max height. Valid values are numbers or heights in px units.string / number——
stripeWhether table is stripedboolean—false
borderWhether table has vertical bordersboolean—false
sizeTable sizestringlarge / default /small—
fitWhether column width fits contentboolean—true
show-headerWhether to show table headerboolean—true
highlight-current-rowWhether to highlight current rowboolean—false
current-row-keyKey of current row, write-only propertystring / number——
row-class-nameFunction for row className, can also use string to set fixed className for all rows.function({ row, rowIndex }) / string——
row-styleFunction for row style, can also use fixed Object to set same Style for all rows.function({ row, rowIndex }) / object——
cell-class-nameFunction for cell className, can also use string to set fixed className for all cells.function({ row, column, rowIndex, columnIndex }) / string——
cell-styleFunction for cell style, can also use fixed Object to set same Style for all cells.function({ row, column, rowIndex, columnIndex }) / object——
header-row-class-nameFunction for header row className, can also use string to set fixed className for all header rows.function({ row, rowIndex }) / string——
header-row-styleFunction for header row style, can also use fixed Object to set same Style for all header rows.function({ row, rowIndex }) / object——
header-cell-class-nameFunction for header cell className, can also use string to set fixed className for all header cells.function({ row, column, rowIndex, columnIndex }) / string——
header-cell-styleFunction for header cell style, can also use fixed Object to set same Style for all header cells.function({ row, column, rowIndex, columnIndex }) / object——
row-keyKey for row data, used to optimize Table rendering; required when using reserve-selection feature and displaying tree data. When type is String, supports multi-level access: user.info.id, but does not support user.info[0].id, use Function for this case.function(row) / string——
empty-textText displayed when data is empty, can also be set through #empty slotstring—No Data
default-expand-allWhether to expand all rows by default, effective when Table contains expandable rows or is tree tableboolean—false
expand-row-keysSet currently expanded rows through this property, requires row-key property to be set, this property is an array of keys for expanded rows.array——
default-sortDefault sort column prop and order. Its prop property specifies default sort column, order specifies default sort orderobject(order: 'ascending' 'descending')'descending')
tooltip-effecteffect of overflow tooltipstringdark / lightdark
tooltip-optionsOptions for overflow tooltip, see tooltip component belowobjectsee tooltipobject
show-summaryWhether to show summary row at table footerboolean—false
sum-textText for first column of summary rowstring—Sum
summary-methodCustom summary calculation methodfunction({ columns, data })——
span-methodMethod for merging rows or columnsfunction({ row, column, rowIndex, columnIndex })——
select-on-indeterminateBehavior when clicking header checkbox when only some rows are selected in multi-select table. If true, select all rows; if false, deselect all rowsboolean—true
indentIndentation of tree nodes when displaying tree datanumber—16
lazyWhether to lazy load child node databoolean——
loadFunction for loading child node data, effective when lazy is truefunction(row, treeNode, resolve)——
tree-propsConfiguration options for rendering nested dataobject—{ hasChildren: 'hasChildren', children: 'children' }
table-layoutLayout method for table cells, rows and columnsstringfixed / autofixed
scrollbar-always-onAlways show scrollbarboolean—false
show-overflow-tooltipWhether to hide extra content and show them in Tooltip when cell content overflows. This will affect all columns.boolean / object—See tooltip-options
flexibleEnsure minimum size of main axis to not exceed contentboolean—false
virtualVirtual scroll configuration, supports high-performance large data renderingboolean / objectSee virtual table documentationfalse

z-table Events ​

Event NameDescriptionCallback Parameters
refreshEvent triggered when pagingpagination
radio-changeEvent triggered when user manually checks Radio of data rowrow
selectEvent triggered when user manually checks Checkbox of data rowselection, row
select-allEvent triggered when user manually checks select all Checkboxselection
selection-changeEvent triggered when selection changesselection
cell-mouse-enterEvent triggered when cell hover entersrow, column, cell, event
cell-mouse-leaveEvent triggered when cell hover leavesrow, column, cell, event
cell-clickEvent triggered when a cell is clickedrow, column, cell, event
cell-dblclickEvent triggered when a cell is double clickedrow, column, cell, event
cell-contextmenuEvent triggered when a cell is right clickedrow, column, cell, event
row-clickEvent triggered when a row is clickedrow, column, event
row-contextmenuEvent triggered when a row is right clickedrow, column, event
row-dblclickEvent triggered when a row is double clickedrow, column, event
header-clickEvent triggered when a column header is clickedcolumn, event
header-contextmenuEvent triggered when a column header is right clickedcolumn, event
sort-changeEvent triggered when table sort conditions change{ column, prop, order }
filter-changeColumn key, if you need to use filter-change event, this attribute is needed to identify which column's filter conditionfilters
current-changeEvent triggered when table's current row changes, if you want to highlight current row, please enable table's highlight-current-row attributecurrentRow, oldCurrentRow
header-dragendEvent triggered when dragging header changes column widthnewWidth, oldWidth, column, event
expand-changeEvent triggered when user expands or collapses a row (when expanding rows, second parameter is expandedRows; when tree table, second parameter is expanded)row, (expandedRows | expanded)

Column Configuration ​

AttributeDescriptionTypeDefault
propField namestring—
labelColumn labelstring—
widthColumn widthstring / number—
min-widthColumn minimum widthstring / number—
fixedWhether column is fixedstring / boolean—
render-headerCustom render header functionFunction—
sortableWhether column is sortableboolean / stringfalse
sort-methodCustom sort methodFunction—
sort-bySort by which propertystring / array / Function—
sort-ordersAvailable sort ordersarray—
resizableWhether column is resizablebooleantrue
formatterFunction for formatting cell contentFunction—
show-overflow-tooltipWhether to show tooltip when content overflowsboolean / object—
alignColumn alignmentstringcenter
header-alignHeader alignmentstring—
class-nameColumn classNamestring—
label-class-nameColumn label classNamestring—
selectableFunction for determining if row is selectableFunction—
reserve-selectionWhether to reserve selection when data changesbooleanfalse
filtersArray of filter optionsarray—
filter-placementPlacement of filter popupstring—
filter-multipleWhether filter allows multiple selectionbooleantrue
filter-methodCustom filter methodFunction—
filtered-valueFilter value for selected filtersarray—

z-table Methods ​

Method NameDescriptionParameters
clearSelectionClear selection for multi-select table—
getSelectionRowsReturn currently selected rows
toggleRowSelectionToggle selection state of a row for multi-select table, can directly set selection state with second parameterrow, selected
toggleAllSelectionToggle select all and deselect all for multi-select table—
toggleRowExpansionToggle row expansion for expandable table or tree table. Use second parameter to directly set expansion staterow, expanded
setCurrentRowSet a row as selected for single-select table, call without parameters to cancel current selectionrow
clearSortClear sort conditions, data will restore to unsorted state—
clearFilterPass array of columnKey to clear filter conditions for specified columns. Clear all filters if no parameterscolumnKeys
doLayoutRe-layout Table. You may need to call this method when table visibility changes to get correct layout—
sortSort table manually. prop parameter specifies sort column, order specifies sort order.prop: string, order: string
scrollToScroll to specific coordinates(options: ScrollToOptions | number, yCoord?: number)
setScrollTopSet vertical scroll positiontop
setScrollLeftSet horizontal scroll positionleft

z-table Slots ​

Slot NameDescriptionSubtags
appendContent inserted after the last row of table. If table has summary row, this slot will be above summary row.—
emptyCustom content when data is empty—
tableTopTop slot—
tableBottomBottom slot—
toolBarTopToolbar top slot—
toolBarBottomToolbar bottom slot—
toolBarRightToolbar right slot—
toolBarLeftToolbar left slot—
tableTitleTable title slot—
paginationTopPagination top slot—
paginationBottomPagination bottom slot—
paginationLeftPagination left slot—
paginationRightPagination right slot—
footerVirtual table footer slot, only available in virtual scroll mode—

pagination Attributes ​

AttributeDescriptionTypeDefault
typePagination typefront / backbonebackbone
pageCurrent pagenumber—
pageSizeNumber of items per pagenumber—
alignPagination positionleft / center / rightright
smallWhether to use small pagination stylebooleantrue
backgroundWhether to add background color to pagination buttonsbooleanfalse
totalTotal number of itemsnumber—
page-countTotal number of pages, set either total or page-count to display page numbers; use total if you need to support page-sizes changesnumber—
pager-countMaximum number of page buttons. When total pages exceed this value, they will be collapsednumber7
layoutComponent layout, sub-component names separated by commasstringprev, pager, next, jumper, ->, total
page-sizesOptions for page size selectorobject[10, 20, 30, 40, 50, 100]
popper-classClass name for page size selector dropdownstring''
prev-textText for previous page button instead of iconstring''
prev-iconIcon for previous page, higher priority than prev-textstring / ComponentArrowLeft
next-textText for next page button instead of iconstring''
next-iconIcon for next page, lower priority than next-textstring / ComponentArrowRight
disabledWhether pagination is disabledbooleanfalse
teleportedWhether to teleport dropdown to bodybooleantrue
hide-on-single-pageWhether to hide when there's only one pagebooleanfalse

editable Attributes ​

AttributeDescriptionTypeDefault
typeEditable table modesingle / multiplesingle
maxLengthMaximum quantitynumber—
deleteConfirmDelete confirmationbooleanfalse
onEditEdit callback({ row, index, column, formRef }) => void—
onCancelCancel callback({ row, index, column, formRef }) => void—
onSaveSave callback({ row, index, column, formRef }) => void—
onDeleteDelete callback({ row, index, column, formRef }) => void—

buttons Attributes in column ​

AttributeDescriptionTypeDefault
typeTypeprimary/ success'/ warning/ danger/ info / dropdown—
labelButton textstring—
childrenDropdown items when type is dropdownarray—
hideButton hideboolean / () => boolean—
onClickClick event({ row, $index, column }) => void—
plainWhether button is plainbooleanfalse
disabledWhether button is disabledboolean / ({ row, $index, column }) => booleanfalse
sizeButton sizedefault / large / small—
textWhether button is text buttonbooleanfalse
bgWhether to show background color for text buttonbooleanfalse
linkWhether button is link buttonbooleanfalse
roundWhether button is roundbooleanfalse
circleWhether button is circlebooleanfalse
loadingWhether button is in loading statebooleanfalse
loading-iconCustom loading icon componentstring / ComponentLoading
iconIcon componentstring / Component—
autofocusNative autofocus attributebooleanfalse
native-typeNative type attributebutton / submit / resetbutton
auto-insert-spaceAutomatically insert space between two Chinese charactersboolean—
colorCustom button color, automatically calculate hover and active colorsstring—
darkDark mode, automatically set color for dark modebooleanfalse
tagCustom element tagstring / Componentbutton

button type dropdown ​

AttributeDescriptionTypeAccepted ValuesDefault
referenceReference textstring / (scope) => VNode—More
onCommandEvent callback triggered when menu item is clicked(command) => void——
typeMenu button type, same as Button component, only effective when split-button is true.string——
sizeMenu size, also affects trigger button when split-button is true.stringlarge / default / smalldefault
max-heightMaximum height of menustring / number——
split-buttonWhether dropdown trigger element is presented as button groupboolean—false
disabledWhether disabledboolean—false
placementMenu popup positionstringtop/top-start/top-end/bottom/bottom-start/bottom-endbottom
triggerBehavior to trigger dropdownstringhover / click /contextmenuhover
hide-on-clickWhether to hide menu after clicking menu itemboolean—true
show-timeoutDelay before showing dropdown menu, only effective when trigger is hovernumber—250
hide-timeoutDelay before hiding dropdown menu (only effective when trigger is hover)number—150
roleARIA attribute for dropdown menu. You might want to change this to "navigation" based on specific scenariosstring—'menu'
tabindextabindex for Dropdown componentnumber—0
popper-classCustom class name for popperstring——
popper-optionspopper.js parametersObjectSee popper.js documentation{modifiers: [{name: 'computeStyles',options: {gpuAcceleration: false}}]}
teleportedWhether to insert dropdown list to body elementboolean—true
AttributeDescriptionTypeAccepted ValuesDefault
disabledWhether disabledboolean / ({ row, $index, column }) => boolean—false
onClickDropdown item click({ row, $index, column }) => void——
dividedWhether to show dividerboolean—false
iconCustom iconstring / Component——

toolBar Attributes ​

AttributeDescriptionTypeAccepted ValuesDefault
excludeTable item label collection not shown in toolbararray——
unCheckDefault unchecked label collectionarray——
refreshWhether refresh feature is displayedboolean—true
densityWhether density feature is displayedboolean—true
fullScreenWhether fullscreen feature is displayedboolean—true
settingWhether column setting feature is displayedboolean—true

Released under the MIT License.