# APID-01 Table — 数据表状态视觉规范 + AI 机读配方（code-first）— Design Spec

> **Date**: 2026-07-22
> **Status**: design approved (owner 2026-07-22) — §6 判断点已定：新增呈现层 props + 选中仅行高亮。spec pending final review
> **Scope**: 数据表**各状态的 TVU 视觉规范**（呈现层，无行为）+ **机读组合配方**（表格页蓝图）。code-first（owner 定不补 Figma）。
> **Backlog**: INFRA-F68 L1 §APID-01（REFRAME 后）。运行时数据网格（排序/筛选/固定列/行选/展开/虚拟滚 = app 层逻辑）**已 owner 裁剪移除，不在本 scope**。
> **前身对话**: owner 2026-07-22 战略裁剪——"当前开发主力是 AI 写代码，手写 Code 便利性非差异化；差异化在 AI 可消费的设计规范"。

---

## 1. 背景与问题

现状 `Table`（`src/canonical/Table.vue` + base `src/components/Table/Table.vue`）是 Figma 保真的**纯呈现表格**：`columns`/`data`/`striped`/`align`/`type` + 可选左右列图标，绑 Figma 6 成员集（Type×Align 单元格，render-verification 12 entries）。它能渲染一张静态数据表，但对 AI 设计产品页时会遇到的**数据表状态一无所知**：

- 数据为空时长什么样？（当前 `data=[]` → 渲染空 tbody，无任何"无数据"提示）
- 加载中长什么样？（无 loading 态）
- 选中的行长什么样？（无选中视觉）
- 某列正在排序时表头长什么样？（有 `showRightIcon` 但无排序 asc/desc 指示语义）
- 一张完整的表格页怎么拼？（表格 + 分页放哪、间距多少——无蓝图）

**这些是 AI 设计产品（能力 4）+ QA 状态矩阵（受众）的真缺口**，不是"开发手写 app 便利性"。运行时**行为**（点表头真去排序、勾选框管理选中集、分页真去切片）由消费方 app 自己实现（AI 写代码时也在 app 层写）——**烘进组件属冗余，owner 已裁剪**。

本 spec 只补**视觉真相 + 机读配方**：让 AI（和人）知道"TVU 风格的数据表在每个状态该长什么样"，并能读到"表格页怎么组合"。

---

## 2. 设计原则（约束）

- **只给视觉状态与配方，不给运行时行为**（owner 2026-07-22 裁剪的核心线）：组件新增的一切都是**呈现层**——由消费方传入的状态**驱动视觉**，组件**不管理状态、不含算法、无 emits、无 v-model**。判据：一个属性/渲染如果需要组件内部维护可变状态或实现数据变换（排序比较、选中集增删、分页切片）→ **出界，不做**；如果只是把 app 传入的状态映射成正确的 TVU 视觉 → 合法。
- **Figma 真源 + 合法 code-first**（FIGMA_AS_SOURCE_OF_TRUTH.md）：现有单元格保真**保留**（render-verification 12 entries 不动）；新增的状态视觉层 Figma 无源 → 走唯一合法 code-first 通道 = **divergence 登记（必带 `user approved code-first 2026-07-22`）**。⚠️ **Table 有 Figma 组件 + render-verif 覆盖，与 UserMenu 纯 code-first 不同 → 绝不加入 `canonical-exempt.json`**（那会丢掉现有 12 entries 保真覆盖）。新 props 全 additive/opt-in，默认渲染不变 → 现有 12 entries 仍有效；`figma-conformance`/`published-vs-code` 若报新非-Figma props，**divergence 条目就是文档化逃生口**，不是 canonical-exempt。
- **向后兼容 v1.0 锁定 API**（API_STABILITY.md）：全部 **additive**，不删/改现有 `columns`/`data`/`striped`/`align`/`type` 及其行为。
- **不烘产品语义**（PROJECT_GOAL §不要做）：空态文案、加载文案走 `TvuLocale` 契约，不写死业务词。
- **原子级完整接线**（AGENTS 硬规则 #8 + pre-commit gate 链）：若动组件公开 API，则一次到位（config + react binding + docs 页 + React demo + slot-boolean 双框架 + divergence + canonical-exempt + affordance + CLAUDE_DESIGN_RULES 收口）。

---

## 3. 交付物

### 3.1 组件呈现层（视觉状态，无行为）—— owner 2026-07-22 定：新增 props

在 `Table` 上新增**纯呈现**能力（全 additive，无 emits/v-model）：

| 能力 | API 形态 | 语义（仅视觉） |
|---|---|---|
| **加载态** | `loading?: boolean`（默认 `false`） | `true` 时 tbody 上覆盖半透明层 + 轻量 CSS spinner（code-first，因无 Spin 组件）。app 自己决定何时置 true。 |
| **空态** | 无新 prop；`data.length === 0 && !loading` 时自动渲染 | 渲染 `#empty` 具名槽；默认 = 居中 `TvuLocale.tableEmpty`（新增 locale key，默认 "No data"）+ 可选空态图标。 |
| **选中行视觉** | `rowKey?: string` + `selectedKeys?: (string \| number)[]` | key ∈ selectedKeys 的行加 `.tbl-row--selected` 视觉（TVU 选中底色）。**组件不管理选中集**，纯反映 app 传入。**仅行高亮，无勾选列**（owner 2026-07-22：勾选框暗示交互、易滑回行为范畴；勾选列留后续按需）。 |
| **排序表头指示** | 列级 `sortOrder?: 'asc' \| 'desc' \| null` | 表头显示 asc/desc 箭头指示（复用 `arrow/sorting`，走 affordance-search 核实）。**组件不排序数据**，只显示"这列当前按 X 序"的指示，排序动作/数据由 app 做。 |

**不做**（明确出界）：点表头触发排序、勾选框 toggle 管理选中集、前导勾选列、`pagination` 内建切片、固定列、展开行、虚拟滚动、任何 emits / v-model / `sort-change` 等事件。

### 3.2 docs 视觉规范页（状态矩阵）

`TablePage.vue` + React descriptor demo 新增状态段，**静态**展示每个状态的 TVU 视觉真相（供 AI/设计/QA 参照）：Empty · Loading · Selected row · Sorted header（asc/desc）· 组合示例（表格 + 下方 `Pagination` 的推荐布局/间距）。React 侧登记 `DESCRIPTOR_FILES`（若走 descriptor demo）。

### 3.3 机读组合配方（AI-consumable recipe）

新增一条 `data-table` 页面级配方，描述如何组合数据表页——供 AI 读取生成产品代码。这是 L5 AIC-01 / L6 PAT-01「机读 recipe/页面蓝图层」的**首个实例**。

- **位置**：优先复用 `component-affordances.json`（现有机读层）加 `recipes` / `page-blueprints` 段；若结构不合，新建 `figma-data/page-recipes.json`（待实现阶段勘察现有 affordances schema 后定，见 §5）。
- **内容**：data-table 页的槽位组合（table + 可选 toolbar + 下方 pagination）+ 每个状态何时出现（empty/loading/selected/sorted）+ 各状态由谁驱动（"app 传 `loading`/`selectedKeys`/列 `sortOrder`；排序/选择/翻页**行为**由 app 实现"）+ 布局/间距 token。
- **收口**：`docs/CLAUDE_DESIGN_RULES.md`（Claude Design bundle 的 SKILL.md 源）补 §Table 数据表用法，否则只读 SKILL.md 的 AI 学不到（UserMenu 本轮实证）。

---

## 4. CE / 框架适配

- 全部 inline（spinner overlay / 空态文字 / 行高亮 / 表头箭头），**无 overlay/teleport** → 保留默认 shadow DOM，不改 `shadowRoot`，不需 light-DOM CE 重构。
- components.config `Table` 条目扩 props（loading / rowKey / selectedKeys + 列 `sortOrder`）+ 新增 `#empty` 具名槽；`events: []` / `vModel: null` 不变。
- `gen:react-bindings` 重生 → React 侧同样 additive。
- demo-slot-boolean（硬规则 #8）：`#empty` slot 投影 + `loading` boolean live 切换，Vue + React 双框架各演示。

## 5. 待实现阶段勘察（不阻塞设计批准）

- 现有 `component-affordances.json` schema 能否容纳 recipe 段（决定 §3.3 位置）。
- 是否已有可复用 empty-state / spinner 视觉（backlog COMP-02 记 Spin/Empty 缺件；本 spec 的 spinner/空态是 Table 内联最小实现，不新建独立组件）。
- `TvuLocale` 契约加 `tableEmpty` key（现有契约已含 pagination/select/confirm/cancel 等，见 INFRA-F68 I18N-01）。

## 6. 判断点（owner 2026-07-22 已定）

1. **组件呈现层状态 props vs 纯 docs**：→ **新增呈现层 props**（§3.1）。状态视觉住组件、跨 consumer 一致可溯源；纯呈现无行为，不属被裁剪的运行时便利。
2. **选中视觉**：→ **仅行高亮**，不做前导勾选列（勾选框暗示交互、易滑回行为范畴；留后续按需）。

---

## 7. 接线清单（若判断点 1 取"新增 props"，实现阶段一次到位）

`src/canonical/Table.vue`（扩 props + `#empty` slot，槽判定用 CE-safe `useHasSlot`，**禁 `$slots`/`useSlots`**——`audit:canonical-slot-guard` 拦）→ components.config Table 条目（L1154-1171，props 1:1 镜像 defineProps + `namedSlots:['empty']`）→ `pnpm gen:react-bindings`（重生 `react-pilot/src/wrappers/Table.tsx` + `types.ts` + `register.ts`，勿手改）→ `src/index.ts`（**无导出变化**，Table 已导出）→ TvuLocale `tableEmpty`（`src/locale/index.ts` interface + defaultLocale 同步）→ affordance（`component-affordances.json` Table 条目加 props/features → `pnpm generate:component-affordances` 重生 md，排序指示图标复用注册表 `action/sorting`，**不改图标注册表**）→ divergence `table-presentational-props-code-first-2026-07-22`（`divergences-decisions.json`，必带 `user approved code-first 2026-07-22`）→ **不加 canonical-exempt**（Table 有 Figma 覆盖，保留 12 render-verif entries）→ Vue docs 状态段（`TablePage.vue`）+ React **手写镜像** demo（`react-pilot/src/demos/Table.tsx`，**非 descriptor**，段标题与 Vue 侧一致，不登记 DESCRIPTOR_FILES）→ demo-slot-boolean 双框架（`#empty` slot + `loading` boolean live）→ CLAUDE_DESIGN_RULES §3 Table 收口 → 机读 recipe → changeset(minor)。

**视觉门**：`.vue`/`.tsx` commit 需 owner `VISUAL_COMMIT_APPROVED`——起 docs 站 + Playwright 截各状态真机图给 owner 审后才 commit。
