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 stripedbooleanfalse
borderWhether table has vertical bordersbooleanfalse
sizeTable sizestringlarge / default /small
fitWhether column width fits contentbooleantrue
show-headerWhether to show table headerbooleantrue
highlight-current-rowWhether to highlight current rowbooleanfalse
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 slotstringNo Data
default-expand-allWhether to expand all rows by default, effective when Table contains expandable rows or is tree tablebooleanfalse
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 footerbooleanfalse
sum-textText for first column of summary rowstringSum
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 rowsbooleantrue
indentIndentation of tree nodes when displaying tree datanumber16
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 scrollbarbooleanfalse
show-overflow-tooltipWhether to hide extra content and show them in Tooltip when cell content overflows. This will affect all columns.boolean / objectSee tooltip-options
flexibleEnsure minimum size of main axis to not exceed contentbooleanfalse
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) => VNodeMore
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 groupbooleanfalse
disabledWhether disabledbooleanfalse
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 itembooleantrue
show-timeoutDelay before showing dropdown menu, only effective when trigger is hovernumber250
hide-timeoutDelay before hiding dropdown menu (only effective when trigger is hover)number150
roleARIA attribute for dropdown menu. You might want to change this to "navigation" based on specific scenariosstring'menu'
tabindextabindex for Dropdown componentnumber0
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 elementbooleantrue
AttributeDescriptionTypeAccepted ValuesDefault
disabledWhether disabledboolean / ({ row, $index, column }) => booleanfalse
onClickDropdown item click({ row, $index, column }) => void
dividedWhether to show dividerbooleanfalse
iconCustom iconstring / Component

toolBar Attributes

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

Released under the MIT License.