Skip to content

Table 表格

通过配置生成表格,内置更适合中后台业务的功能。

TIP

某些属性在z-table内部被默认配置,例如:align 设置为 center

基础用法

配置column生成表格项。el-table相关属性直接在z-table组件上传入,el-table-column属性在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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])
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>

表格标题

配置title属性生成表格标题,支持字符串和函数类型,也可使用tableTitle插槽自定义。

<!-- 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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])

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="表格标题"
    @refresh="getTableData"
  />
</template>

操作按钮

column中配置操作项,type传入button,配置buttons数组。按钮属性直接在操作项中配置即可。

<!-- 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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
  {
    type: 'button',
    label: '操作',
    buttons: [
      {
        type: 'primary',
        link: true,
        label: '编辑',
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'edit')
        },
      },
      {
        type: 'danger',
        label: '删除',
        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>

操作按钮也支持动态属性,如:disabled等。传入一个方法,参数为当前行相关数据。

<!-- 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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
  {
    type: 'button',
    label: '操作',
    buttons: [
      {
        type: 'primary',
        link: true,
        label: '编辑',
        disabled: ({ row, column, $index }: TableColumnScopeData<RowData>) => row.name === 'Steven',
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'edit')
        },
      },
      {
        type: 'danger',
        link: true,
        label: '删除',
        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>

操作项下拉

如需下拉,可以在buttons数组中配置typedropdownchildren中配置下拉选项

<!-- 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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
  {
    type: 'button',
    label: '操作',
    width: '200px',
    buttons: [
      {
        type: 'primary',
        link: true,
        label: '编辑',
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'edit')
        },
      },
      {
        type: 'danger',
        link: true,
        label: '删除',
        onClick: ({ row }: TableColumnScopeData<RowData>) => {
          console.log(row, 'delete')
        },
      },
      {
        type: 'dropdown',
        reference: '删除',
        children: [
          {
            type: 'primary',
            link: true,
            label: '复制',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'copy')
            },
          },
          {
            type: 'danger',
            link: true,
            label: '操作',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'operate')
            },
          },
        ],
      },
    ],
  },
])
</script>

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

reference字段配置下拉关联文案(默认为更多),支持函数和字符串类型。

支持配置el-dropdownel-dropdown-item组件属性和方法。

<!-- 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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
  {
    type: 'button',
    label: '操作',
    buttons: [
      {
        type: 'dropdown',
        reference: '操作',
        placement: 'top-start',
        children: [
          {
            type: 'primary',
            link: true,
            label: '编辑',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'edit')
            },
          },
          {
            type: 'danger',
            link: true,
            label: '删除',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'delete')
            },
          },
        ],
      },
      {
        type: 'dropdown',
        reference: () => h('span', { style: { cursor: 'pointer' } }, '操作2'),
        placement: 'top',
        onVisibleChange: (visible: boolean) => {
          console.log(visible, 'visible')
        },
        children: [
          {
            type: 'primary',
            link: true,
            label: '复制',
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'copy')
            },
          },
          {
            type: 'danger',
            link: true,
            label: '操作',
            divided: true,
            onClick: ({ row }: TableColumnScopeData<RowData>) => {
              console.log(row, 'operate')
            },
          },
        ],
      },
    ],
  },
])
</script>

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

分页

配置pagination,支持双向绑定,实现分页效果。layout默认配置为total, sizes, prev, pager, next, jumperpageSizes默认配置为[100, 200, 300, 400, 500],支持自定义。

<!-- 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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])

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>

配置paginationLeftpaginationRightpaginationToppaginationBottom插槽实现分页上下左右内容自定义。

<!-- 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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])

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>

前端分页

配置paginationtypefront,开启前端分页功能,totalData字段传入所有数据。

pageSize0paginationfalsepagination不传,分页不展示。

[]
<!-- 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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])

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>

列显隐

column中配置hide字段,支持函数或布尔值,函数返回布尔值,true表示隐藏,false表示显示。

<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: '姓名',
    hide: () => isHide.value,
  },
  {
    prop: 'gender',
    label: '性别',
    hide: true,
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])

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

<template>
  <el-button @click="changeVisible">
    点击修改列显隐
  </el-button>
  <z-table
    :data="tableData"
    :columns="columns"
  />
</template>

列自定义

column中配置slotrender实现自定义列内容。

<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: '姓名',
    render: ({ row }: TableColumnScopeData<RowData>) => h('span', row.name),
  },
  {
    prop: 'gender',
    label: '性别',
    slot: 'gender',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])
</script>

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

列类型

column中配置type实现表格列类型,支持expandradioselectionindex

支持自定义列组件,配置component字段,支持inputselectdatepickerswitch、任意局部或全局注册组件。

TIP

typeradio或者需要跨页选中checkbox时,需要配合rowKey使用(默认id)。

TIP

component直接传入组件,请使用markRaw包裹,防止影响性能。

<!-- 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: '姓名',
    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: '性别',
    fieldProps: {
      clearable: true,
    },
    onChange: ({ row, column, $index }: TableColumnScopeData<RowData>, val: string) => {
      console.log('change event', row, column, $index, val)
    },
  },
  {
    prop: 'age',
    label: '年龄',
    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: '出生日期',
  },
])

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

<template>
  <z-table v-model:data="tableData" :columns="columns" :options="options">
    <template #expand>
      <span>展开内容</span>
    </template>
  </z-table>
</template>

动态属性

column自定义内容支持动态属性,disabledplaceholder等组件属性支持传入函数(函数属性暂不支持,如:ElInput组件的formatter属性),参数为当前行scope数据。

<!-- 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: '姓名',
    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: '性别',
    onChange: ({ row }: TableColumnScopeData<RowData>, val: string) => {
      console.log('change event', row, val)
    },
    fieldProps: {
      placeholder: ({ row, column, $index }: TableColumnScopeData<RowData>) => {
        return `${row.name}性别${$index}`
      },
      clearable: true,
    },
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])

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

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

表格头自定义

column中将label配置为带slotSlot的字符串或配置为render函数实现自定义列表头。

<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', '自定义表头'),
  },
  {
    prop: 'gender',
    label: 'genderHeaderSlot',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])
</script>

<template>
  <z-table
    :data="tableData"
    :columns="columns"
  >
    <template #genderHeaderSlot="scope">
      <span>性别自定义表头{{ scope.$index }}</span>
    </template>
  </z-table>
</template>

列提示

column中配置tooltip实现表头提示功能,支持函数和字符串。

<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: '姓名',
    tooltip: () => h('span', '姓名提示'),
  },
  {
    prop: 'gender',
    label: '性别',
    tooltip: '性别提示',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])
</script>

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

工具栏

toolBar 配置项,用于配置表格的工具栏。

toolBar值为false,不展示工具栏。

如果想配置某个功能不展示,配置toolBar下的四个字段refreshdensityfullScreensettingfalse即可。

<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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])
</script>

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

toolBar支持配置默认不选中,配置uncheck字段,值为column项的label

<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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])
</script>

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

toolBar支持排除某些表格项,配置exclude字段,值为column项的label

<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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])
</script>

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

toolBar上下左右内容支持自定义,通过toolBarToptoolBarBottomtoolBarRighttoolBarLefttableTitle插槽配置。

<!-- 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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])

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>

数据拖拽

设置draggabletrue,开启数据拖拽。

WARNING

必须配置row-key,否则会出现更新问题。

<!-- 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: '排序',
  },
  {
    prop: 'name',
    label: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])
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>

支持设置slotrender自定义拖拽图标。

<!-- 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: '排序',
  },
  {
    prop: 'name',
    label: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])

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设置为true,开启表格编辑模式,该字段支持布尔值或对象类型,可编辑表格type默认为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: '姓名',
  },
  {
    component: 'select',
    prop: 'gender',
    label: '性别',
  },
  {
    component: 'input',
    prop: 'age',
    label: '年龄',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: '出生日期',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
])

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', 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>

editable设置为typemultiple,开启多行编辑模式。

配置editablemaxLength,可以设置最大新增数量。配置editabledeleteConfirmtrue,可开启删除二次确认。

<!-- 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: '姓名',
  },
  {
    component: 'select',
    prop: 'gender',
    label: '性别',
  },
  {
    component: 'input',
    prop: 'age',
    label: '年龄',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: '出生日期',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
])

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', 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>

配置editableonSaveonDeleteonEditonCancel

<!-- 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: '姓名',
  },
  {
    component: 'select',
    prop: 'gender',
    label: '性别',
  },
  {
    component: 'input',
    prop: 'age',
    label: '年龄',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: '出生日期',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
])

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', 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>

支持自定义操作按钮。

<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: '姓名',
  },
  {
    component: 'select',
    prop: 'gender',
    label: '性别',
  },
  {
    component: 'input',
    prop: 'age',
    label: '年龄',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: '出生日期',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
  {
    type: 'button',
    label: '操作',
    buttons: ({ renderEdit, renderCancel, renderDelete, renderSave }: DefaultButtonOperation, tableData: Ref<RowData[]>) => {
      return [
        {
          type: 'primary',
          link: true,
          label: '复制',
          hide: ({ row }: TableColumnScopeData<RowData>) => row.__isEdit,
          onClick: ({ row }: TableColumnScopeData<RowData>) => {
            tableData.value.push({ ...row })
          },
        },
        renderEdit,
        renderCancel,
        renderDelete,
        renderSave,
      ]
    },
  },
])

const options = {
  gender: [
    { label: '男', value: '1' },
    { label: '女', value: '2' },
  ],
}

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

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

吸顶

通过配置sticky属性的topparent(会出现滚动条的DOM元素)zIndex实现吸顶功能。 top默认为50pxzIndex默认为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: '姓名',
    width: 300,
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
    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>

支持通过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: '姓名',
    width: 300,
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
    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,详情可参考z-watermark配置。

<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: '姓名',
  },
  {
    component: 'select',
    prop: 'gender',
    label: '性别',
  },
  {
    component: 'input',
    prop: 'age',
    label: '年龄',
  },
  {
    component: 'el-date-picker',
    prop: 'time',
    label: '出生日期',
    fieldProps: {
      valueFormat: 'YYYY-MM-DD',
    },
  },
])

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

<template>
  <z-table
    :data="tableData"
    :columns="columns"
    :options="options"
    watermark="表格水印"
  />
</template>

表格方法

z-table的表格方法按照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: '姓名',
  },
  {
    prop: 'gender',
    label: '性别',
  },
  {
    prop: 'age',
    label: '年龄',
  },
  {
    prop: 'time',
    label: '出生日期',
  },
])

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">
    清空选中
  </el-button>
  <z-table
    ref="zTableRef"
    :data="tableData"
    :columns="columns"
    @radio-change="handleRadioChange"
    @selection-change="handleSelectionChange"
  />
</template>

z-table属性

属性名说明类型可选值默认值
modelValue:data显示的数据,支持双向绑定array
modelValue:pagination分页配置,支持双向绑定object
loading表格加载boolean
title表格标题string / function
columns表格配置项array
toolBar工具栏配置object / boolean
editable可编辑表格配置object / boolean
options表格内部选项数据源object
watermark水印配置object(具体配置可查看z-watermark文档)
sticky表格头吸顶配置object
options表格内部选项数据源object
totalData表格所有数据(前端分页生效)array
heightTable 的高度, 默认为自动高度。 如果 height 为 number 类型,单位 px;如果 height 为 string 类型,则这个高度会设置为 Table 的 style.height 的值,Table 的高度会受控于外部样式。string / number
max-heightTable 的最大高度。 合法的值为数字或者单位为 px 的高度。string / number
stripe是否为斑马纹 tablebooleanfalse
border是否带有纵向边框booleanfalse
sizeTable 的尺寸stringlarge / default /small
fit列的宽度是否自撑开booleantrue
show-header是否显示表头booleantrue
highlight-current-row是否要高亮当前行booleanfalse
current-row-key当前行的 key,只写属性string / number
row-class-name行的 className 的回调方法,也可以使用字符串为所有行设置一个固定的 className。function({ row, rowIndex }) / string
row-style行的 style 的回调方法,也可以使用一个固定的 Object 为所有行设置一样的 Style。function({ row, rowIndex }) / object
cell-class-name单元格的 className 的回调方法,也可以使用字符串为所有单元格设置一个固定的 className。function({ row, column, rowIndex, columnIndex }) / string
cell-style单元格的 style 的回调方法,也可以使用一个固定的 Object 为所有单元格设置一样的 Style。function({ row, column, rowIndex, columnIndex }) / object
header-row-class-name表头行的 className 的回调方法,也可以使用字符串为所有表头行设置一个固定的 className。function({ row, rowIndex }) / string
header-row-style表头行的 style 的回调方法,也可以使用一个固定的 Object 为所有表头行设置一样的 Style。function({ row, rowIndex }) / object
header-cell-class-name表头单元格的 className 的回调方法,也可以使用字符串为所有表头单元格设置一个固定的 className。function({ row, column, rowIndex, columnIndex }) / string
header-cell-style表头单元格的 style 的回调方法,也可以使用一个固定的 Object 为所有表头单元格设置一样的 Style。function({ row, column, rowIndex, columnIndex }) / object
row-key行数据的 Key,用来优化 Table 的渲染; 在使用reserve-selection功能与显示树形数据时,该属性是必填的。 类型为 String 时,支持多层访问:user.info.id,但不支持 user.info[0].id,此种情况请使用 Functionfunction(row) / string
empty-text空数据时显示的文本内容, 也可以通过 #empty 设置stringNo Data
default-expand-all是否默认展开所有行,当 Table 包含展开行存在或者为树形表格时有效booleanfalse
expand-row-keys可以通过该属性设置 Table 目前的展开行,需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。array
default-sort默认的排序列的 prop 和顺序。 它的 prop 属性指定默认的排序的列,order 指定默认排序的顺序object(order: 'ascending' 'descending')'descending')
tooltip-effect溢出的 tooltip 的 effectstringdark / lightdark
tooltip-options溢出 tooltip 的选项,参见下述 tooltip 组件object请参考 tooltipobject
show-summary是否在表尾显示合计行booleanfalse
sum-text显示摘要行第一列的文本stringSum
summary-method自定义的合计计算方法function({ columns, data })
span-method合并行或列的计算方法function({ row, column, rowIndex, columnIndex })
select-on-indeterminate在多选表格中,当仅有部分行被选中时,点击表头的多选框时的行为。 若为 true,则选中所有行;若为 false,则取消选择所有行booleantrue
indent展示树形数据时,树节点的缩进number16
lazy是否懒加载子节点数据boolean
load加载子节点数据的函数,lazy 为 true 时生效function(row, treeNode, resolve)
tree-props渲染嵌套数据的配置选项object{ hasChildren: 'hasChildren', children: 'children' }
table-layout设置表格单元、行和列的布局方式stringfixed / autofixed
scrollbar-always-on总是显示滚动条booleanfalse
show-overflow-tooltip是否隐藏额外内容并在单元格悬停时使用 Tooltip 显示它们。这将影响全部列的展示。boolean \object参考 tooltip-options
flexible确保主轴的最小尺寸,以便不超过内容booleanfalse

z-table事件

事件名说明回调参数
refresh翻页时触发的事件pagination
radio-change当用户手动勾选数据行的 Radio 时触发的事件row
select当用户手动勾选数据行的 Checkbox 时触发的事件selection, row
select-all当用户手动勾选全选 Checkbox 时触发的事件selection
selection-change当选择项发生变化时会触发该事件selection
cell-mouse-enter当单元格 hover 进入时会触发该事件row, column, cell, event
cell-mouse-leave当单元格 hover 退出时会触发该事件row, column, cell, event
cell-click当某个单元格被点击时会触发该事件row, column, cell, event
cell-dblclick当某个单元格被双击击时会触发该事件row, column, cell, event
cell-contextmenu当某个单元格被鼠标右键点击时会触发该事件row, column, cell, event
row-click当某一行被点击时会触发该事件row, column, event
row-contextmenu当某一行被鼠标右键点击时会触发该事件row, column, event
row-dblclick当某一行被双击时会触发该事件row, column, event
header-click当某一列的表头被点击时会触发该事件column, event
header-contextmenu当某一列的表头被鼠标右键点击时触发该事件column, event
sort-change当表格的排序条件发生变化的时候会触发该事件{ column, prop, order }
filter-changecolumn 的 key, 如果需要使用 filter-change 事件,则需要此属性标识是哪个 column 的筛选条件filters
current-change当表格的当前行发生变化的时候会触发该事件,如果要高亮当前行,请打开表格的 highlight-current-row 属性currentRow, oldCurrentRow
header-dragend当拖动表头改变了列的宽度的时候会触发该事件newWidth, oldWidth, column, event
expand-change当用户对某一行展开或者关闭的时候会触发该事件(展开行时,回调的第二个参数为 expandedRows;树形表格时第二参数为 expanded)row, (expandedRows | expanded)

z-table方法

方法名说明参数
clearSelection用于多选表格,清空用户的选择
getSelectionRows返回当前选中的行
toggleRowSelection用于多选表格,切换某一行的选中状态, 如果使用了第二个参数,则可直接设置这一行选中与否row, selected
toggleAllSelection用于多选表格,切换全选和全不选
toggleRowExpansion用于可扩展的表格或树表格,如果某行被扩展,则切换。 使用第二个参数,您可以直接设置该行应该被扩展或折叠。row, expanded
setCurrentRow用于单选表格,设定某一行为选中行, 如果调用时不加参数,则会取消目前高亮行的选中状态。row
clearSort用于清空排序条件,数据会恢复成未排序的状态
clearFilter传入由columnKey 组成的数组以清除指定列的过滤条件。 如果没有参数,清除所有过滤器columnKeys
doLayout对 Table 进行重新布局。 当表格可见性变化时,您可能需要调用此方法以获得正确的布局
sort手动排序表格。 参数 prop 属性指定排序列,order 指定排序顺序。prop: string, order: string
scrollTo滚动到一组特定坐标(options: ScrollToOptions | number, yCoord?: number)
setScrollTop设置垂直滚动位置top
setScrollLeft设置水平滚动位置left

z-table插槽

插槽名说明子标签
append插入至表格最后一行之后的内容, 如果需要对表格的内容进行无限滚动操作,可能需要用到这个 slot。 若表格有合计行,该 slot 会位于合计行之上。
empty当数据为空时自定义的内容
tableTop顶部插槽
tableBottom底部插槽
toolBarTop工具栏顶部插槽
toolBarBottom工具栏底部插槽
toolBarRight工具栏右部插槽
toolBarLeft工具栏左侧左部插槽
tableTitle表格标题插槽
paginationTop分页顶部插槽
paginationBottom分页底部插槽
paginationLeft分页左侧插槽
paginationRight分页右侧插槽

columns属性

属性名说明类型可选值默认值
type对应列的类型。stringselection / index / expand/ radio / button
component列组件型。stringinput / select / checkbox / radio / 任意局部或全局注册组件
index如果设置了 type=index,可以通过传递 index 属性来自定义索引number / function(index)
label显示的标题(建议配置)string / (scope) => VNode
buttons按钮配置array
options选项组件数据源array
tooltip列提示string / () => VNode
column-keycolumn 的 key, column 的 key, 如果需要使用 filter-change 事件,则需要此属性标识是哪个 column 的筛选条件string
prop字段名称 对应列内容的字段名, 也可以使用 property属性string
width对应列的宽度string / number
min-width对应列的最小宽度, 对应列的最小宽度, 与 width 的区别是 width 是固定的,min-width 会把剩余宽度按比例分配给设置了 min-width 的列string / number
fixed列是否固定在左侧或者右侧。 true 表示固定在左侧string / booleantrue / 'left' / 'right'
render-header列标题 Label 区域渲染使用的 Functionfunction({ column, $index })
sortable对应列是否可以排序, 如果设置为 'custom',则代表用户希望远程排序,需要监听 Table 的 sort-change 事件boolean / stringcustomfalse
sort-method指定数据按照哪个属性进行排序,仅当sortable设置为true的时候有效。 应该如同 Array.sort 那样返回一个 Numberfunction(a, b)
sort-by指定数据按照哪个属性进行排序,仅当 sortable 设置为 true 且没有设置 sort-method 的时候有效。 如果 sort-by 为数组,则先按照第 1 个属性排序,如果第 1 个相等,再按照第 2 个排序,以此类推function(row, index) / string / array
sort-orders数据在排序时所使用排序策略的轮转顺序,仅当 sortable 为 true 时有效。 需传入一个数组,随着用户点击表头,该列依次按照数组中元素的顺序进行排序array数组中的元素需为以下三者之一:ascending 表示升序,descending 表示降序,null 表示还原为原始顺序['ascending', 'descending', null]
resizable对应列是否可以通过拖动改变宽度(需要在 el-table 上设置 border 属性为真)booleantrue
formatter用来格式化内容function(row, column, cellValue, index)
show-overflow-tooltip当内容过长被隐藏时显示 tooltipboolean \object参考表格的 tooltip-options
align对齐方式stringleft / center / rightleft
header-align表头对齐方式, 若不设置该项,则使用表格的对齐方式stringleft / center / right
class-name列的 classNamestring
label-class-name当前列标题的自定义类名string
selectable仅对 type=selection 的列有效,类型为 Function,Function 的返回值用来决定这一行的 CheckBox 是否可以勾选function(row, index)
reserve-selection数据刷新后是否保留选项,仅对 type=selection 的列有效, 请注意, 需指定 row-key 来让这个功能生效。booleanfalse
filters数据过滤的选项, 数组格式,数组中的元素需要有 text 和 value 属性。 数组中的每个元素都需要有 text 和 value 属性。Array<{text: string, value: string}>
filter-placement过滤弹出框的定位string与 Tooltip 的 placement 属性相同
filter-multiple数据过滤的选项是否多选booleantrue
filter-method数据过滤使用的方法, 如果是多选的筛选项,对每一条数据会执行多次,任意一次返回 true 就会显示。function(value, row, column)
filtered-value选中的数据过滤项,如果需要自定义表头过滤的渲染方式,可能会需要此属性。array

pagination属性

属性名说明类型默认值
type分页类型front / backbonebackbone
page当前页number
pageSize每页显示条目个数number
small是否使用小型分页样式booleantrue
background是否为分页按钮添加背景色booleanfalse
total总条目数number
page-count总页数, totalpage-count 设置任意一个就可以达到显示页码的功能;如果要支持 page-sizes 的更改,则需要使用 total 属性number
pager-count设置最大页码按钮数。 页码按钮的数量,当总页数超过该值时会折叠number7
layout组件布局,子组件名用逗号分隔stringprev, pager, next, jumper, ->, total
page-sizes每页显示个数选择器的选项设置object[10, 20, 30, 40, 50, 100]
popper-class每页显示个数选择器的下拉框类名string''
prev-text替代图标显示的上一页文字string''
prev-icon上一页的图标, 比 prev-text 优先级更高string / ComponentArrowLeft
next-text替代图标显示的下一页文字string''
next-icon下一页的图标, 比 next-text 优先级更低string / ComponentArrowRight
disabled是否禁用分页booleanfalse
teleported是否将下拉菜单teleport至 bodybooleantrue
hide-on-single-page只有一页时是否隐藏booleanfalse

editable属性

属性名说明类型默认值
type可编辑表格模式single / multiplesingle
maxLength最大数量number
deleteConfirm删除二次确认booleanfalse
onEdit编辑回调({ row, index, column, formRef }) => void
onCancel取消回调({ row, index, column, formRef }) => void
onSave保存回调({ row, index, column, formRef }) => void
onDelete删除回调({ row, index, column, formRef }) => void

column中buttons属性

属性名说明类型默认值
type类型primary/ success'/ warning/ danger/ info / dropdown
label文案string
childrentypedropdown生效,下拉项array
hide按钮隐藏boolean / () => boolean
onClick点击事件({ row, $index, column }) => void
plain是否为朴素按钮booleanfalse
disabled按钮是否为禁用状态boolean / ({ row, $index, column }) => booleanfalse
size尺寸default / large / small
plain是否为朴素按钮booleanfalse
text是否为文字按钮booleanfalse
bg是否显示文字按钮背景颜色booleanfalse
link是否为链接按钮booleanfalse
round是否为圆角按钮booleanfalse
circle是否为圆形按钮booleanfalse
loading是否为加载中状态booleanfalse
loading-icon自定义加载中状态图标组件string / ComponentLoading
icon图标组件string / Component
autofocus原生 autofocus 属性booleanfalse
native-type原生 type 属性button / submit / resetbutton
auto-insert-space自动在两个中文字符之间插入空格boolean
color自定义按钮颜色, 并自动计算 hoveractive 触发后的颜色string
darkdark 模式, 意味着自动设置 color 为 dark 模式的颜色booleanfalse
tag自定义元素标签string / Componentbutton

button类型为dropdown

属性名说明类型可选值默认值
reference关联文案string / (scope) => VNode更多
onCommand点击菜单项触发的事件回调(command) => void
type菜单按钮类型,同 Button 组件一样,仅在 split-button 为 true 的情况下有效。string
size菜单尺寸,在 split-button 为 true 的情况下也对触发按钮生效。stringlarge / default / smalldefault
max-height菜单最大高度string / number
split-button下拉触发元素呈现为按钮组booleanfalse
disabled是否禁用booleanfalse
placement菜单弹出位置stringtop/top-start/top-end/bottom/bottom-start/bottom-endbottom
trigger触发下拉的行为stringhover / click /contextmenuhover
hide-on-click是否在点击菜单项后隐藏菜单booleantrue
show-timeout展开下拉菜单的延时,仅在 trigger 为 hover 时有效number250
hide-timeout收起下拉菜单的延时(仅在 trigger 为 hover 时有效)number150
role下拉菜单的 ARIA 属性。 根据具体场景,您可能想要将此更改为“navigation”string'menu'
tabindexDropdown 组件的 tabindexnumber0
popper-class自定义浮层类名string
popper-optionspopper.js 参数Object请参考 popper.js 文档{modifiers: [{name: 'computeStyles',options: {gpuAcceleration: false}}]}
teleported是否将下拉列表插入至 body 元素booleantrue

button类型为dropdown的children下拉项属性

属性名说明类型可选值默认值
disabled是否禁用boolean / ({ row, $index, column }) => booleanfalse
onClick下拉项点击({ row, $index, column }) => void
divided是否显示分隔符booleanfalse
icon自定义图标string / Component

toolBar属性

属性名说明类型可选值默认值
exclude不显示在工具栏的表格项 label 集合array
unCheck默认不选中的 label 集合array
refresh刷新功能是否展示booleantrue
density密度功能是否展示booleantrue
fullScreen全屏功能是否展示booleantrue
setting列配置功能是否展示booleantrue

Released under the MIT License.