Table
数据表格。列定义驱动渲染,支持排序、斑马纹、边框、固定表头、加载中与空数据状态。
基础
| 路径 | 方法 | 状态码 | 耗时 |
|---|---|---|---|
| GET /api/users | GET | 200 | 12ms |
| POST /api/orders | POST | 201 | 45ms |
| GET /api/products | GET | 200 | 8ms |
| PUT /api/users/1 | PUT | 500 | 230ms |
| GET /api/not-found | GET | 404 | 3ms |
vue
<script setup lang="ts">
import { HTable } from 'happier-ui'
import type { HTableColumn } from 'happier-ui'
const columns: HTableColumn[] = [
{ key: 'name', title: '路径' },
{ key: 'method', title: '方法', width: 80, align: 'center' },
{ key: 'status', title: '状态码', width: 80, align: 'right' },
{ key: 'time', title: '耗时', width: 80, align: 'right' },
]
const data = [
{ id: '1', name: 'GET /api/users', status: 200, method: 'GET', time: '12ms' },
{ id: '2', name: 'POST /api/orders', status: 201, method: 'POST', time: '45ms' },
{ id: '3', name: 'GET /api/products', status: 200, method: 'GET', time: '8ms' },
]
</script>
<template>
<h-table :columns="columns" :data="data" />
</template>泛型行类型
HTable 是泛型组件。传入具体行类型 T(普通 interface,无需索引签名),data 标注为 T[]、columns 标注为 HTableColumn<T>[] 后:column.key 会约束为 keyof T,#cell slot 的 row 推断为 T,无需任何 as 断言。
vue
<script setup lang="ts">
import { HTable } from 'happier-ui'
import type { HTableColumn } from 'happier-ui'
interface RequestLog {
id: string
name: string
status: number
method: string
time: string
}
const columns: HTableColumn<RequestLog>[] = [
{ key: 'name', title: '路径', sortable: true },
{ key: 'status', title: '状态码', align: 'right', sortable: true },
]
const data: RequestLog[] = [
{ id: '1', name: 'GET /api/users', status: 200, method: 'GET', time: '12ms' },
]
</script>
<template>
<h-table :columns="columns" :data="data">
<!-- row 推断为 RequestLog;row.status 是 number,无需断言 -->
<template #cell="{ column, row }">
<span v-if="column.key === 'status'">{{ row.status >= 400 ? '错误' : '正常' }}</span>
<template v-else>{{ row[column.key] }}</template>
</template>
</h-table>
</template>不标注类型参数时(裸 HTableColumn[] / data 传 Record<string, unknown>[]),T 默认为 Record<string, unknown>,与旧用法完全兼容。
变体
bordered + striped
| 路径 | 方法 | 状态码 | 耗时 |
|---|---|---|---|
| GET /api/users | GET | 200 | 12ms |
| POST /api/orders | POST | 201 | 45ms |
| GET /api/products | GET | 200 | 8ms |
| PUT /api/users/1 | PUT | 500 | 230ms |
| GET /api/not-found | GET | 404 | 3ms |
vue
<h-table :columns="columns" :data="data" bordered striped />排序
当前排序:
| 路径 | 方法 | 状态码 | 耗时 |
|---|---|---|---|
| GET /api/users | GET | 200 | 12ms |
| POST /api/orders | POST | 201 | 45ms |
| GET /api/products | GET | 200 | 8ms |
| PUT /api/users/1 | PUT | 500 | 230ms |
| GET /api/not-found | GET | 404 | 3ms |
vue
<script setup>
const sortState = ref(null)
</script>
<template>
<h-table
:columns="columns"
:data="data"
@sort="sortState = $event"
/>
</template>加载中
| 路径 | 方法 | 状态码 | 耗时 |
|---|---|---|---|
| GET /api/users | GET | 200 | 12ms |
| POST /api/orders | POST | 201 | 45ms |
| GET /api/products | GET | 200 | 8ms |
| PUT /api/users/1 | PUT | 500 | 230ms |
| GET /api/not-found | GET | 404 | 3ms |
vue
<h-table :columns="columns" :data="data" loading />空数据
| 路径 | 方法 | 状态码 | 耗时 |
|---|
暂无数据
vue
<h-table :columns="columns" :data="[]" />自定义单元格
| 路径 | 方法 | 状态码 | 耗时 |
|---|---|---|---|
| GET /api/users | GET | 200 | 12ms |
| POST /api/orders | POST | 201 | 45ms |
| GET /api/products | GET | 200 | 8ms |
| PUT /api/users/1 | PUT | 500 | 230ms |
| GET /api/not-found | GET | 404 | 3ms |
vue
<h-table :columns="columns" :data="data" bordered striped>
<template #cell="{ column, row }">
<span v-if="column.key === 'status'" :style="{ color: statusColor(row.status) }">
{{ row.status }}
</span>
<template v-else>{{ row[column.key] }}</template>
</template>
</h-table>API
Props
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
columns | HTableColumn<T>[] | [] | 列定义(T 为行类型,默认 Record<string, unknown>) |
data | T[] | [] | 数据行;标注具体 interface[] 即可获得类型推断 |
rowKey | string | ((row) => string) | 'id' | 行唯一标识 |
bordered | boolean | false | 列间竖线边框 |
striped | boolean | false | 斑马纹 |
stickyHeader | boolean | false | 表头固定(需父容器限制高度并 overflow-y: auto) |
emptyText | string | '暂无数据' | 空数据文案 |
loading | boolean | false | 加载中态 |
类型
ts
// 泛型;默认 T = Record<string, unknown>(裸用法兼容)
interface HTableColumn<T extends object = Record<string, unknown>> {
key: keyof T & string // 约束到行字段名
title: string
width?: string | number // 如 '120px' 或 120
align?: 'left' | 'center' | 'right'
sortable?: boolean
render?: (row: T, index: number) => string | number
}
interface HTableSort {
key: string
order: 'asc' | 'desc'
}Emits
| 事件 | 载荷 | 说明 |
|---|---|---|
sort | HTableSort | null | 排序列变化时触发;null 表示取消排序 |
Slots
| 名称 | 作用域 | 说明 |
|---|---|---|
cell | { column, row, index } | 自定义单元格内容 |
empty | — | 自定义空状态(默认显示 emptyText) |
loading | — | 自定义加载中状态 |
行为说明
- 排序为受控行为:组件仅维护 UI 排序指示图标,点击可排序列触发
sortemit,由父组件处理实际排序逻辑。 - 排序状态切换:未排序 → 升序 → 降序 → 取消排序(按列头循环)。
- 行 key 通过
rowKeyprop 指定(字符串 key 名或函数);默认取row.id。 render函数返回 string/number,用于简单格式化;复杂渲染请使用#cellslot。loading时若 data 有内容,显示 loading overlay 覆盖数据区;若 data 为空,显示 loading 指示。- 空数据时通过
#emptyslot 自定义空状态,默认降级为HEmpty。
无障碍
- 原生
<table>/<thead>/<tbody>/<th>/<td>语义。 - 排序按钮通过
role="columnheader"隐式语义;排序图标aria-hidden。 - 加载中 spinner 使用
aria-label="加载中"。 - 空状态通过
HEmpty提供标题语义。