Table
Компонент таблицы, управляемый данными. Передавайте columns и data как JSON, настраивайте ячейки через scoped-слоты.
Basic
<script setup>
import { VxTable } from 'veloxui'
// Define table columns — each column needs a unique "key" matching a field in the data
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
{ key: 'joined', label: 'Joined' },
]
// Row data — each object should have keys matching the column "key" values
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active', joined: 'Jan 4, 2024' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active', joined: 'Feb 12, 2024' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive', joined: 'Mar 8, 2024' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active', joined: 'Apr 22, 2024' },
{ name: 'Sofia Davis', email: 'sofia.davis@email.com', role: 'Admin', status: 'Pending', joined: 'May 15, 2024' },
]
</script>
<template>
<!-- The simplest usage: just pass columns and data -->
<VxTable :columns="columns" :data="data" />
<!-- You can customize any cell using a slot named after the column key -->
<VxTable :columns="columns" :data="data">
<!-- Slot props: row = full row object, value = cell value, index = row index -->
<template #name="{ row }">
<strong>{{ row.name }}</strong>
</template>
<template #email="{ value }">
<span style="font-family: monospace; font-size: 13px;">{{ value }}</span>
</template>
<template #status="{ value }">
<span :style="{ color: value === 'Active' ? '#22c55e' : '#a1a1aa' }">
{{ value }}
</span>
</template>
</VxTable>
</template>Render Function
Используйте свойство render у столбца, чтобы напрямую возвращать HTML — слоты не нужны. Отлично подходит для быстрого форматирования без шаблонного кода.
<script setup>
import { VxTable } from 'veloxui'
// The "render" property returns an HTML string — no slot needed
const columns = [
{
key: 'name',
label: 'Name',
render: (row) => `<strong>${row.name}</strong>`,
},
{
key: 'status',
label: 'Status',
render: (row) => {
const colors = { Active: '#22c55e', Inactive: '#a1a1aa', Pending: '#f59e0b' }
const color = colors[row.status] || '#a1a1aa'
return `<span style="color:${color}">${row.status}</span>`
},
},
{ key: 'email', label: 'Email' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', status: 'Active' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', status: 'Active' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', status: 'Inactive' },
{ name: 'William Kim', email: 'will@email.com', status: 'Pending' },
]
</script>
<template>
<VxTable :columns="columns" :data="data" bordered />
</template>Bordered
Добавьте проп bordered для внешней границы, скруглённых углов и разделителей столбцов.
<script setup>
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive' },
]
</script>
<template>
<!-- "bordered" adds outer border, rounded corners, and column dividers -->
<VxTable :columns="columns" :data="data" bordered />
</template>Striped
Чередующиеся фоны строк с помощью пропа striped.
<script setup>
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active' },
{ name: 'Sofia Davis', email: 'sofia.davis@email.com', role: 'Admin', status: 'Pending' },
]
</script>
<template>
<!-- "striped" alternates row background colors for readability -->
<VxTable :columns="columns" :data="data" striped bordered />
</template>Fixed Header
Установите max-height, чтобы тело таблицы прокручивалось, а шапка автоматически оставалась зафиксированной.
<script setup>
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
]
// When there are many rows, use max-height to make the body scrollable
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active' },
{ name: 'Sofia Davis', email: 'sofia.davis@email.com', role: 'Admin', status: 'Pending' },
{ name: 'Emma Wilson', email: 'emma.wilson@email.com', role: 'Editor', status: 'Active' },
{ name: 'Noah Brown', email: 'noah.brown@email.com', role: 'Viewer', status: 'Inactive' },
{ name: 'Ava Garcia', email: 'ava.garcia@email.com', role: 'Admin', status: 'Active' },
{ name: 'Ethan Miller', email: 'ethan.m@email.com', role: 'Editor', status: 'Pending' },
{ name: 'Mia Anderson', email: 'mia.anderson@email.com', role: 'Viewer', status: 'Active' },
{ name: 'Lucas Taylor', email: 'lucas.t@email.com', role: 'Admin', status: 'Active' },
{ name: 'Charlotte Thomas', email: 'charlotte.t@email.com', role: 'Editor', status: 'Inactive' },
]
</script>
<template>
<!-- max-height keeps the header fixed while the body scrolls -->
<VxTable :columns="columns" :data="data" :max-height="400" bordered />
</template>Fixed Columns
Установите fixed: 'left' или fixed: 'right' у столбца, чтобы закрепить его, пока остальные прокручиваются горизонтально. Для закреплённых столбцов требуется width.
<script setup>
import { VxTable } from 'veloxui'
// Use "fixed" to pin columns. Requires "width" on fixed columns.
const columns = [
{ key: 'name', label: 'Name', fixed: 'left', width: 180 },
{ key: 'email', label: 'Email', width: 250 },
{ key: 'role', label: 'Role', width: 150 },
{ key: 'department', label: 'Department', width: 180 },
{ key: 'location', label: 'Location', width: 180 },
{ key: 'status', label: 'Status', width: 150 },
{ key: 'joined', label: 'Joined', width: 150 },
{ key: 'actions', label: '', fixed: 'right', width: 80 },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', department: 'Engineering', location: 'San Francisco', status: 'Active', joined: 'Jan 4, 2024' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', department: 'Marketing', location: 'New York', status: 'Active', joined: 'Feb 12, 2024' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', department: 'Design', location: 'London', status: 'Inactive', joined: 'Mar 8, 2024' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', department: 'Engineering', location: 'Tokyo', status: 'Active', joined: 'Apr 22, 2024' },
]
</script>
<template>
<!-- "Name" stays pinned on the left, "actions" on the right, rest scrolls -->
<VxTable :columns="columns" :data="data" bordered :max-height="350">
<template #actions>
<button style="background: none; border: none; cursor: pointer;">···</button>
</template>
</VxTable>
</template>Selection
Добавьте selectable, чтобы показать чекбоксы. Используйте v-model:selected для связывания выбранных значений. По умолчанию используется поле id — установите selection-key, чтобы изменить его.
<script setup>
import { ref } from 'vue'
import { VxTable } from 'veloxui'
const selected = ref([])
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor' },
]
</script>
<template>
<!-- Show which rows are selected -->
<p>Selected: {{ selected.length ? selected.join(', ') : 'none' }}</p>
<!-- "selectable" adds checkboxes, "selection-key" sets which field is used as the value -->
<VxTable
:columns="columns"
:data="data"
selectable
v-model:selected="selected"
selection-key="name"
bordered
/>
<!-- selected = ['Olivia Martin', 'Jackson Lee'] when those rows are checked -->
</template>Drag & Drop
Добавьте draggable, чтобы включить переупорядочивание строк. Используйте v-model:data для получения обновлённого порядка.
<script setup>
import { ref } from 'vue'
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
]
// Data must be a ref so the table can update the order after drag
const data = ref([
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active' },
{ name: 'Sofia Davis', email: 'sofia.davis@email.com', role: 'Admin', status: 'Pending' },
])
</script>
<template>
<!-- "draggable" enables row reordering by drag & drop -->
<!-- v-model:data syncs the new order back to your data ref -->
<VxTable :columns="columns" v-model:data="data" draggable bordered />
</template>Resizable Columns
Добавьте проп resizable, чтобы пользователи могли перетаскивать границы столбцов для изменения их размера. Также можно установить resizable для каждого столбца в его определении.
<script setup>
import { VxTable } from 'veloxui'
// Set "width" on columns to define initial widths for resizing
const columns = [
{ key: 'name', label: 'Name', width: 200 },
{ key: 'email', label: 'Email', width: 260 },
{ key: 'role', label: 'Role', width: 140 },
{ key: 'status', label: 'Status', width: 140 },
{ key: 'joined', label: 'Joined', width: 160 },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active', joined: 'Jan 4, 2024' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active', joined: 'Feb 12, 2024' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive', joined: 'Mar 8, 2024' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active', joined: 'Apr 22, 2024' },
]
</script>
<template>
<!-- Object form: set min/max width limits for all columns -->
<VxTable :columns="columns" :data="data" :resizable="{ minWidth: 80, maxWidth: 500 }" bordered />
<!-- Boolean form: enable resizing with default limits (minWidth: 50) -->
<VxTable :columns="columns" :data="data" resizable bordered />
</template>Sorting
Добавьте sortable, чтобы сделать заголовки столбцов кликабельными. Клик циклически переключает по возрастанию, по убыванию и без сортировки. Используйте v-model:sort для управляемой сортировки или позвольте таблице управлять ею внутренне.
<script setup>
import { ref } from 'vue'
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active' },
]
// Optional: control sort state externally
const sort = ref({ key: 'name', order: 'asc' })
</script>
<template>
<!-- "sortable" makes all column headers clickable to sort -->
<VxTable :columns="columns" :data="data" sortable bordered />
<!-- Controlled: bind sort state with v-model:sort -->
<VxTable :columns="columns" :data="data" sortable v-model:sort="sort" bordered />
<!-- Per-column: only make specific columns sortable -->
<VxTable
:columns="[
{ key: 'name', label: 'Name', sortable: true },
{ key: 'email', label: 'Email' },
]"
:data="data"
bordered
/>
</template>Loading
Добавьте проп loading, чтобы показать оверлей со спиннером.
<script setup>
import { ref } from 'vue'
import { VxTable, VxButton } from 'veloxui'
const loading = ref(false)
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor' },
]
// Simulate a loading state
function toggleLoading() {
loading.value = true
setTimeout(() => { loading.value = false }, 2000)
}
</script>
<template>
<VxButton size="sm" variant="outline" @click="toggleLoading">Toggle loading</VxButton>
<!-- "loading" shows a spinner overlay on top of the table -->
<VxTable :columns="columns" :data="data" :loading="loading" bordered />
</template>Expandable Rows
Добавьте expandable, чтобы показать переключатель развёртывания на каждой строке. Предоставьте слот expand для содержимого деталей. Использует selection-key (по умолчанию id) для отслеживания развёрнутых строк.
<script setup>
import { ref } from 'vue'
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', joined: 'Jan 4, 2024' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', joined: 'Feb 12, 2024' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', joined: 'Mar 8, 2024' },
]
// Optional: track which rows are expanded externally
const expanded = ref([])
</script>
<template>
<!-- "expandable" adds an expand toggle to each row -->
<!-- "selection-key" determines which field uniquely identifies each row -->
<VxTable :columns="columns" :data="data" expandable selection-key="name" bordered>
<!-- The "expand" slot renders content below the row when expanded -->
<template #expand="{ row }">
<div>
<strong>{{ row.name }}</strong> joined on {{ row.joined }} as {{ row.role }}.
<br />Contact: {{ row.email }}
</div>
</template>
</VxTable>
<!-- Controlled: track expanded rows with v-model:expanded -->
<VxTable
:columns="columns"
:data="data"
expandable
v-model:expanded="expanded"
selection-key="name"
bordered
>
<template #expand="{ row }">
<div>Details for {{ row.name }}</div>
</template>
</VxTable>
</template>Pagination
Добавьте pagination, чтобы показать элементы управления страницами. Передайте true для встроенного состояния или объект для управляемой пагинации.
<script setup>
import { ref } from 'vue'
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active' },
{ name: 'Sofia Davis', email: 'sofia.davis@email.com', role: 'Admin', status: 'Pending' },
{ name: 'Emma Wilson', email: 'emma.wilson@email.com', role: 'Editor', status: 'Active' },
{ name: 'Noah Brown', email: 'noah.brown@email.com', role: 'Viewer', status: 'Inactive' },
{ name: 'Ava Garcia', email: 'ava.garcia@email.com', role: 'Admin', status: 'Active' },
]
// Optional: control pagination state (page, pageSize, available sizes)
const page = ref({ page: 1, pageSize: 5, pageSizes: [5, 10, 25] })
</script>
<template>
<!-- "pagination" as boolean: uses built-in state, defaults to 10 per page -->
<VxTable :columns="columns" :data="data" pagination bordered />
<!-- Controlled: bind pagination state with v-model:pagination -->
<VxTable :columns="columns" :data="data" v-model:pagination="page" bordered />
</template>Global Filter
Передайте строку filter, чтобы искать по всем видимым столбцам.
<script setup>
import { ref } from 'vue'
import { VxTable, VxInput } from 'veloxui'
const filterText = ref('')
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active' },
]
</script>
<template>
<!-- Use VxInput to capture the search text -->
<VxInput v-model="filterText" placeholder="Search..." size="sm" style="max-width: 260px;" />
<!-- "filter" searches across all visible columns and hides non-matching rows -->
<VxTable :columns="columns" :data="data" :filter="filterText" bordered />
</template>Row Grouping
Установите group-by в ключ столбца, чтобы сгруппировать строки со сворачиваемыми заголовками.
<script setup>
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive' },
{ name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active' },
{ name: 'Sofia Davis', email: 'sofia.davis@email.com', role: 'Admin', status: 'Pending' },
]
</script>
<template>
<!-- "group-by" groups rows by the value of the specified column key -->
<!-- Rows with the same "role" will be grouped under collapsible headers -->
<VxTable :columns="columns" :data="data" group-by="role" bordered />
</template>Column Visibility
Добавьте проп column-toggle, чтобы показать встроенный выпадающий список видимости столбцов. Список автоматически заполняется из определения columns с использованием мультиселекта.
<script setup>
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
{ key: 'joined', label: 'Joined' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active', joined: 'Jan 4, 2024' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active', joined: 'Feb 12, 2024' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive', joined: 'Mar 8, 2024' },
]
</script>
<template>
<!-- "column-toggle" adds a dropdown to show/hide columns -->
<VxTable :columns="columns" :data="data" column-toggle bordered />
</template>Column Reorder
Передайте v-model:column-order, чтобы включить переупорядочивание столбцов перетаскиванием.
<script setup>
import { ref } from 'vue'
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
{ key: 'status', label: 'Status' },
{ key: 'joined', label: 'Joined' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active', joined: 'Jan 4, 2024' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active', joined: 'Feb 12, 2024' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive', joined: 'Mar 8, 2024' },
]
// Column order as an array of column keys — drag headers to reorder
const order = ref(['name', 'email', 'role', 'status', 'joined'])
</script>
<template>
<!-- v-model:column-order enables drag-and-drop column reordering -->
<VxTable :columns="columns" :data="data" v-model:column-order="order" bordered />
</template>Inline Editing
Установите editable: true у столбца, чтобы включить редактирование по двойному клику. Слушайте @cell-edit, чтобы обновлять данные.
<script setup>
import { ref } from 'vue'
import { VxTable } from 'veloxui'
// Set "editable: true" on columns you want to be editable
const columns = [
{ key: 'name', label: 'Name', editable: true },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role', editable: true },
{ key: 'status', label: 'Status' },
]
// Each row needs a unique "id" field (used internally to identify the row)
const data = ref([
{ id: 1, name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin', status: 'Active' },
{ id: 2, name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor', status: 'Active' },
{ id: 3, name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer', status: 'Inactive' },
{ id: 4, name: 'William Kim', email: 'will@email.com', role: 'Editor', status: 'Active' },
])
// Handle cell edits — update the data when a cell value changes
function onCellEdit(key, row, value) {
const item = data.value.find(r => r.id === row.id)
if (item) item[key] = value
}
</script>
<template>
<!-- Double-click an editable cell to edit it -->
<!-- Press Enter to save, Escape to cancel -->
<VxTable :columns="columns" :data="data" @cell-edit="onCellEdit" bordered />
</template>Remote Data
Передайте функцию fetch-data, чтобы загружать данные с бесконечной прокруткой — как в выпадающем списке select. Таблица загружает первую страницу при монтировании, затем подгружает новые строки по мере прокрутки к низу. При изменении сортировки или фильтра загрузка начинается заново с первой страницы.
<script setup>
import { VxTable } from 'veloxui'
const columns = [
{ key: 'id', label: 'ID', width: 80 },
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
]
// "fetchData" receives { page, pageSize, sort, filter }
// Must return { data: [...rows], total: totalRowCount }
// The table calls this automatically:
// - On mount (page 1)
// - When scrolling near the bottom (next page, appended)
// - When sort or filter changes (resets to page 1)
async function fetchUsers({ page, pageSize }) {
const res = await fetch(`/api/users?page=${page}&limit=${pageSize}`)
const json = await res.json()
return { data: json.items, total: json.total }
}
</script>
<template>
<!-- "fetch-data" enables server-side data with infinite scroll -->
<!-- Set "max-height" to make the table scrollable -->
<VxTable
:columns="columns"
:fetch-data="fetchUsers"
:max-height="350"
sortable
bordered
/>
</template>Row Click
Слушайте событие @row-click, чтобы обрабатывать клики по строкам.
<script setup>
import { ref } from 'vue'
import { VxTable } from 'veloxui'
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' },
]
const data = [
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', role: 'Admin' },
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', role: 'Editor' },
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', role: 'Viewer' },
]
const clickedRow = ref(null)
function handleRowClick(row, index) {
clickedRow.value = row
console.log('Clicked row:', row, 'at index:', index)
}
</script>
<template>
<p v-if="clickedRow">Выбрано: {{ clickedRow.name }}</p>
<!-- @row-click fires when any row is clicked -->
<VxTable :columns="columns" :data="data" @row-click="handleRowClick" bordered />
</template>Summary Row
Используйте проп footer, чтобы добавить строку итогов/сводки. Ячейки футера поддерживают те же слоты footer-[key] для пользовательского содержимого.
<script setup>
import { VxTable } from 'veloxui'
// Use "align" to right-align numeric columns
const columns = [
{ key: 'product', label: 'Product' },
{ key: 'qty', label: 'Qty', align: 'right' },
{ key: 'price', label: 'Price', align: 'right' },
{ key: 'total', label: 'Total', align: 'right' },
]
const data = [
{ product: 'MacBook Pro 14"', qty: 2, price: '$1,999.00', total: '$3,998.00' },
{ product: 'AirPods Pro', qty: 5, price: '$249.00', total: '$1,245.00' },
{ product: 'Magic Keyboard', qty: 3, price: '$299.00', total: '$897.00' },
{ product: 'Studio Display', qty: 1, price: '$1,599.00', total: '$1,599.00' },
{ product: 'iPhone 15 Pro', qty: 4, price: '$999.00', total: '$3,996.00' },
]
// "footer" has the same shape as data — it renders as a sticky bottom row
const footer = [
{ product: 'Total (5 items)', qty: 15, price: '', total: '$11,735.00' },
]
</script>
<template>
<VxTable :columns="columns" :data="data" :footer="footer" bordered>
<!-- Customize footer cells with "footer-[key]" slots -->
<template #footer-product="{ value }">
<span style="font-weight: 600;">{{ value }}</span>
</template>
<template #footer-total="{ value }">
<span style="font-weight: 700;">{{ value }}</span>
</template>
</VxTable>
</template>API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
columns | TableColumn[] | — | Определения столбцов (обязательно) |
data | Record<string, any>[] | [] | Массив данных строк |
footer | Record<string, any>[] | — | Данные строки футера (того же вида, что и data) |
bordered | boolean | false | Внешняя граница, скруглённые углы, разделители столбцов |
striped | boolean | false | Чередующиеся фоны строк |
height | string | number | — | Фиксированная высота с закреплённой шапкой |
maxHeight | string | number | — | Максимальная высота с закреплённой шапкой |
caption | string | — | Текст заголовка таблицы |
scrollbar | 'auto' | 'hover' | 'always' | 'hidden' | 'hover' | Видимость полосы прокрутки |
selectable | boolean | false | Показывать чекбоксы строк |
selected | any[] | — | Выбранные значения (v-model) |
selectionKey | string | 'id' | Поле строки, используемое как ключ выбора/развёртывания |
draggable | boolean | false | Включить переупорядочивание строк перетаскиванием |
resizable | boolean | TableResizable | false | Включить изменение размера столбцов |
sortable | boolean | false | Включить сортировку столбцов |
sort | TableSort | null | — | Состояние сортировки (v-model). Если опущено, используется внутреннее состояние |
loading | boolean | false | Показать оверлей спиннера загрузки |
expandable | boolean | false | Показать переключатель развёртывания в каждой строке |
expanded | any[] | — | Ключи развёрнутых строк (v-model). Если опущено, используется внутреннее состояние |
pagination | boolean | TablePagination | false | Включить пагинацию. true для встроенной, объект для управляемой |
filter | string | '' | Глобальный текстовый фильтр по всем видимым столбцам |
groupBy | string | — | Ключ столбца для группировки строк |
columnToggle | boolean | false | Показать встроенный выпадающий список видимости столбцов |
columnOrder | string[] | — | Порядок ключей столбцов (v-model). Включает переупорядочивание перетаскиванием |
fetchData | (params: TableFetchParams) => Promise<TableFetchResult> | — | Серверные данные с бесконечной прокруткой. Подгружает при прокрутке, перезагружает при изменении сортировки/фильтра |
pageCount | number | 0 | Максимальное число страниц для загрузки. 0 означает безлимит (останавливается, когда data.length >= total) |
scrollDistance | number | 50 | Расстояние от низа (px) для запуска подгрузки |
TableColumn
| Property | Type | Default | Description |
|---|---|---|---|
key | string | — | Ключ поля данных (обязательно) |
label | string | — | Текст заголовка (обязательно) |
width | string | number | — | Ширина столбца |
align | 'left' | 'center' | 'right' | 'left' | Выравнивание текста |
fixed | 'left' | 'right' | — | Закрепить столбец у левого или правого края |
resizable | boolean | TableResizable | — | Переопределить resizable на уровне таблицы для этого столбца |
sortable | boolean | — | Переопределить sortable на уровне таблицы для этого столбца |
editable | boolean | — | Включить встроенное редактирование по двойному клику |
render | (row, index) => string | — | HTML-функция рендеринга для содержимого ячейки |
TableResizable
| Property | Type | Default | Description |
|---|---|---|---|
minWidth | number | 50 | Минимальная ширина (px) |
maxWidth | number | Infinity | Максимальная ширина (px) |
TableSort
| Property | Type | Description |
|---|---|---|
key | string | Ключ сортируемого столбца |
order | 'asc' | 'desc' | Направление сортировки |
TablePagination
| Property | Type | Default | Description |
|---|---|---|---|
page | number | — | Текущая страница (с 1) |
pageSize | number | — | Строк на странице |
total | number | — | Общее число элементов (для серверной пагинации) |
pageSizes | number[] | [10, 20, 50, 100] | Варианты размера страницы |
TableFetchParams
| Property | Type | Description |
|---|---|---|
page | number | Текущая страница (с 1) |
pageSize | number | Строк на странице |
sort | TableSort | null | Текущее состояние сортировки |
filter | string | Текущий текст фильтра |
TableFetchResult
| Property | Type | Description |
|---|---|---|
data | Record<string, any>[] | Строки для текущей страницы |
total | number | Общее число элементов (все страницы) |
Slots
| Slot | Props | Description |
|---|---|---|
[column.key] | { row, value, index } | Пользовательское содержимое ячейки |
header-[column.key] | { column } | Пользовательская ячейка заголовка |
footer-[column.key] | { row, value, index } | Пользовательская ячейка футера |
expand | { row, index } | Содержимое деталей развёрнутой строки |
expand-icon | { row, expanded } | Пользовательская иконка переключателя развёртывания |
group | { group, count } | Пользовательское содержимое заголовка группы |
caption | — | Пользовательский заголовок таблицы |
empty | — | Пользовательское пустое состояние |
Events
| Event | Payload | Description |
|---|---|---|
update:selected | any[] | Выбор изменён |
update:data | Record<string, any>[] | Порядок строк изменён (перетаскиванием) |
update:sort | TableSort | null | Состояние сортировки изменено |
update:expanded | any[] | Развёрнутые строки изменены |
update:pagination | TablePagination | Page/pageSize изменены |
update:columnOrder | string[] | Порядок столбцов изменён (перетаскиванием) |
row-click | (row, index) | Клик по строке |
cell-edit | (key, row, value) | Значение ячейки отредактировано |
column-resize | (key, width) | Размер столбца изменён |