SwTable

SwTable 是管理台数据表格:columns 配置驱动表头与列序,单元格默认渲染 item[key],复杂内容(缩略图 / 状态徽章 / 操作按钮组)走 #cell-<key> 作用域插槽由业务自渲染;内置管理台高频三大件——受控列排序(v-model:sort)、行选择批量(v-model:selected)、loading 过渡,以及斑马纹 / 密度 / 粘性表头视觉旋钮。视觉对齐 shadcn Table 基调。

语义 <table> 直出(SEO / a11y),根包一层 .sw-table 容器做 overflow-x: auto——窄屏时表格自身横向滚动、不撑爆页面。基线样式(边框 + 圆角 / 表头弱化底 / 行 hover / 单元格 padding)走 --sw-table-* token,applier 覆盖变量即换肤。

边界:分页 / 筛选不内置——用 SwPagination / SwSegmented / SwSelect 页面层组合;排序只管交互不排数据(受控,见下);固定列 / 列宽拖拽 / 树形 / 虚拟滚动等重功能等真实数据点再议。这对齐 framework「只管布局不绑业务 UI」铁律。

基础用法

基础列(缺省单元格取 item[key])

加载中...
<script setup lang="ts">
const COLUMNS = [
  { key: 'name', label: '名称' },
  { key: 'category', label: '分类' },
  { key: 'count', label: '集数', width: '4rem', align: 'center' },
]
const rows = [{ id: 1, name: '未来都市', category: '动画', count: 12 }]
</script>

<template>
  <SwTable :columns="COLUMNS" :items="rows" />
</template>

单元格 / 表头插槽(运营台列表形态)

#cell-<key> 按列自定义单元格(作用域 { item, value, index }),#header-<key> 自定义表头(作用域 { column }):

状态徽章 + 行内操作(#cell-* 插槽)

加载中...

空态占位

#empty 在 items 为空时渲染为一行整宽占位;不传则空 tbody:

#empty 空态占位

加载中...

列排序(受控)

column.sortable 让列头可点,三态循环 asc → desc → 无 并经 v-model:sort 受控回显(表头渲染指示箭头 + aria-sort)。组件不排数据——业务拿到排序态后本地排(toSorted)或带参调后端(管理台主流是服务端排序):

列排序(受控三态,本 demo 本地排序)

加载中...
<script setup lang="ts">
const sort = ref<SwTableSort | undefined>()
// 服务端排序:排序态变化即带参重新拉数据
watch(sort, (s) => refetch({ orderBy: s?.key, order: s?.order }))
</script>

<template>
  <SwTable v-model:sort="sort" :columns="COLUMNS" :items="rows" />
</template>

行选择与批量操作

selectable 渲染 checkbox 选择列 + 表头全选(部分选中时半选 indeterminate),选中集合走 v-model:selected(rowKey 值数组):

行选择 + 批量操作

加载中...

loading 过渡态

loading 让表格半透明 + 遮罩转圈,保留结构不闪白——服务「已有数据、翻页 / 筛选刷新中」的过渡;首屏无数据的 loading 仍归页面层四态(骨架屏),两者互补:

翻页刷新过渡(loading + SwPagination 组合)

加载中...

视觉旋钮:斑马纹与密度

striped 偶数行弱化底、size="compact" 收紧单元格 padding,数据密集页常用组合:

斑马纹 + 紧凑密度(可切换对比)

加载中...

粘性表头(长列表容器内滚动)

sticky-header + --sw-table-max-height 让容器变滚动区、表头钉在顶部:

粘性表头(30 行容器内滚动)

加载中...

管理台综合形态

排序 + 行选批量 + loading + 行点击 + 状态徽章 + 行内操作的完整组合(真实运营台列表页形态):

管理台综合(全能力组合)

加载中...

与分页组合(页面层数据流)

表格不管分页——页码翻页用 SwPagination 受控替换 items:

<template>
  <SwTable :columns="COLUMNS" :items="pageItems">
    <template #empty>暂无数据</template>
  </SwTable>
  <SwPagination v-model:page="page" :total="total" :page-size="20" />
</template>

Props

名称类型必填默认值说明
columnsSwTableColumn[]✓—列配置,每项 { key, label?, width?, align?, sortable? }
itemsT[]✓—行数据(泛型,组件不关心形状)
rowKeystring | ((item: T) => string | number)'id'行 key 字段名或取值函数;解析不出回退 index
stripedbooleanfalse斑马纹(偶数行弱化底)
size'default' | 'compact''default'密度(compact 收紧单元格 padding)。⚠️ 与 SwThemeSwitch / SwLangSwitch 的 size(图标 px 数值)不是一回事——那两个是尺寸、这个是密度枚举,对齐 Ant / Element 的 Table.size
stickyHeaderbooleanfalse粘性表头(配 --sw-table-max-height 让容器变滚动区)
loadingbooleanfalse刷新过渡态(遮罩 + 半透明,保留结构)
selectablebooleanfalse渲染 checkbox 选择列 + 表头全选
sort(v-model:sort)SwTableSort | undefinedundefined当前排序态;undefined = 无排序
selected(v-model:selected)(string | number)[][]选中行的 rowKey 值集合

SwTableColumn:{ key: string; label?: string; width?: string; align?: 'left' \| 'center' \| 'right'; sortable?: boolean }——key 既是缺省单元格取值字段,也是 #cell-<key> / #header-<key> 插槽名后缀;width 落到 <th>(CSS 宽度值);align 默认 'left';sortable 让列头可点排序。

SwTableSort:{ key: string; order: 'asc' \| 'desc' }。两个类型均可从包名导入:import type { SwTableColumn, SwTableSort } from '@swift-web/framework'。

Events

名称携带说明
update:sortSwTableSort | undefined点 sortable 列头触发(asc → desc → undefined 三态循环)
update:selected(string | number)[]行选 / 全选变化
row-click(item, index)点击行触发(行内 button / a / input 等 interactive 元素的点击不触发);有监听时行自动 cursor: pointer

行内操作(编辑 / 上下架 / 删除)仍在 #cell-<key> 插槽内用 SwButton 等自带事件。

Slots

名称作用域参数说明
cell-<key>{ item, value, index }逐列自定义单元格。不传则渲染 String(item[key] ?? '')
header-<key>{ column }逐列自定义表头。不传则渲染 column.label。⚠️ 可排序列的插槽内容会被包进 <button>(键盘可达),故只能放 phrasing content——塞 <button> / <a> 会构成嵌套交互元素
empty—items 为空时渲染为一行整宽占位(可选;不传则空 tbody)

样式定制 Token

表格皮肤经 --sw-table-* token 覆盖(fallback 即默认视觉):

Token默认值作用点
--sw-table-bgvar(--card)表格底色
--sw-table-bordervar(--border)外框与行分隔线颜色
--sw-table-radiusvar(--radius)表格圆角
--sw-table-head-colorvar(--muted-foreground)表头文字色
--sw-table-head-bgcolor-mix(in srgb, var(--foreground) 6%, transparent)表头底色(foreground 叠加:任意主题下与行底可辨)
--sw-table-hover-bgcolor-mix(in srgb, var(--primary) 4%, transparent)行 hover 底色
--sw-table-striped-bgcolor-mix(in srgb, var(--foreground) 3%, transparent)斑马纹偶数行底色(striped)
--sw-table-cell-padding0.75rem 1rem单元格内边距
--sw-table-cell-padding-compact0.375rem 0.75rem紧凑密度内边距(size="compact")
--sw-table-font-sizeinherit表格字号
--sw-table-max-heightnone容器最大高(设值即容器内滚动,配 stickyHeader)

注意事项

  • 可排序表头是真 <button>:sortable 列的表头内容被包进 <button>(W3C ARIA APG 的 Sortable Table 范式),键盘可 Tab 聚焦、Enter / Space 排序,aria-sort 仍在 <th> 上。想改 hover 视觉请选 button.th-inner:hover。
  • row-click 是便捷层,不是唯一通路:行本身有意不做成可聚焦元素(长表会凭空多出等量 Tab 停靠点)。行内必须有承担同一动作的真实可聚焦元素(详情链接 / 操作按钮),否则键盘与读屏用户根本触达不了该动作——那是 WCAG 2.1.1 Keyboard(Level A)不达标,且鼠标测试永远看不出来。dev 期会 console.warn 提示,生产构建整段被移除。
  • 排序不排数据:v-model:sort 只是受控交互态——业务必须自己排(本地 toSorted 或带参调后端),否则点了列头「只有箭头变、数据不动」。
  • loading 与四态的分工:loading 服务「已有数据刷新中」的过渡;首屏无数据的 loading / error / empty 仍归页面层四态(骨架屏 / 重试入口),#empty 仅是空数据行的轻量兜底。
  • 行内操作不触发 row-click:#cell-* 内的 button / a / input 点击不会冒泡成行点击,无需手动 stopPropagation。
  • 不内置分页 / 筛选:用 SwPagination / SwSegmented / SwSelect 页面层组合。
  • 行内异步操作记得锁 pending:#cell-* 里的操作按钮遵守 SB-001(async @click 配 :loading),如上下架按行 id 锁。
  • 窄屏横向滚动是内置行为:.sw-table 容器自带 overflow-x: auto,列多时给列设 width 保证可读宽度即可,别在外层再包滚动容器。

相关