diff --git a/docs/list-table-scroll-demo.html b/docs/list-table-scroll-demo.html
new file mode 100644
index 000000000..999f5308c
--- /dev/null
+++ b/docs/list-table-scroll-demo.html
@@ -0,0 +1,804 @@
+
+
+
+
+
+
列表横向 / 纵向滚动体验 Demo
+
+ 对齐 web-admin Fresh Mint。用下方 Tab 切换「现状问题」与三种改法,
+ 请在灰色模拟视口内纵向滚动到中部、再试横向滚动,感受差异。
+
+
+ 问题 1:横滚条在表最底部,行多时够不着
+ 问题 2:纵滚到中部后看不到表头
+ 目标:横滚条常在可视区底部 · 表头始终可见
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
现状:整页拉长,表跟着页面滚
+
与多数业务列表一致:表格无固定高度,横向滚动发生在整张表底部。
+
+
模拟视口
请先向下滚半屏
+
+
+ 复现:① 纵向滚到行 20+ → 表头消失;② 想横看「操作」列 → 必须滚到最底才能碰到横滚条。
+
+
+ 已选 0 项
+
+
+
+
+
+
+
+
+
+
+
方案 A:表格视口填满高度(内嵌滚动)
+
+ 对齐规范:动态 height 使表体尽量撑满,
+ 分页始终可见且下方留 10px;
+ 纵滚在表体内,表头 sticky,横滚条贴表体可视区底。
+
+
+
实现成本低
与 EP 官方行为一致
+
+
+ 一次解决两个问题:表头始终在表体顶部;横滚条始终在表体可视区底部(不必滚到第 60 行);分页下约 10px。
+
+
+ 已选 0 项
+
+
+
+
+
+
+
+
+
+
+
方案 B:保留整页滚动 + sticky 表头 + 底部悬浮横滚条
+
+ 表仍可随页面变高;用 sticky 钉住表头;在滚动容器底部再放一条「镜像」横滚条,始终可见。
+
+
+
交互灵活
实现与同步成本更高
+
+
+ 适合「不想裁切表体高度、希望一屏看更多行」的场景;需维护真实横滚与悬浮条 scrollLeft 双向同步。
+
+
+ 已选 0 项
+
+
+
+
+
+
+
+
+
+
+
方案 C:表体内嵌滚动 + 顶部镜像横滚条
+
+ 在方案 A 基础上,表头上方再放一条横滚条:人眼在看标题附近时也能横移,无需移到表底。
+
+
+
A 的增强
双条同步,略占高度
+
+
+ 横滚可达性最好(顶/底都能拖)。若采用 A,可按「列特别多」的页面再叠加顶部条。
+
+
+ 已选 0 项
+
+
+
+
+
+
+
+
+
与现有代码的关系
+
+ 规范 §7.6 已写 max-height + overflow;HTML demo 也有 sticky 表头。
+ 业务页多数未给 el-table 设高度,bx 的 .table-scroll 仅 overflow-x,故问题仍在。
+
+
+
+
推荐落地路径
+
+ 优先方案 A:公共列表用 useListTableViewportHeight 填满视口(分页下 10px),分页留在表外。
+ 列极多的页可再加方案 C 顶条。
+
+
+
+
注意点
+
+ 固定高度后筛选区展开会挤表体,需用「视口剩余高度」动态计算;
+ 展开行 / 树表要回归测 doLayout。
+
+
+
+
+
+
看完后请选择倾向方案(可点选,并在对话里告诉我)
+
+
+
A表体内嵌滚动
+
成本最低,一次修两个问题;与 Element Plus / 规范最贴合。
+
+
+
Bsticky + 悬浮横条
+
保留「整表随页面变高」;同步逻辑与兼容成本更高。
+
+
+
CA + 顶部镜像条
+
在 A 上增强横滚可达性;适合列特别宽的列表。
+
+
+
+
+
+
+
+
diff --git a/docs/superpowers/plans/2026-09-01-web-admin-list-table-viewport-scroll.md b/docs/superpowers/plans/2026-09-01-web-admin-list-table-viewport-scroll.md
new file mode 100644
index 000000000..cded498bf
--- /dev/null
+++ b/docs/superpowers/plans/2026-09-01-web-admin-list-table-viewport-scroll.md
@@ -0,0 +1,650 @@
+# web-admin 列表表体内嵌滚动(视口填满)Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 让 web-admin 主列表在视口内表体内嵌滚动:表头固定、横滚条贴在表体可视区底,分页始终可见且距内容区底 10px。
+
+**Architecture:** 抽出纯函数 `calcListTableViewportHeight`;composable `useListTableViewportHeight` 测量 `#app-content` 底、表顶、分页高并绑定 `ListTable`/`el-table` 的 `:height`;先接模板页与 bx 样板,再文档/OpenSpec,最后按模块批推。
+
+**Tech Stack:** Vue 3 · `@vueuse/core` · Element Plus `el-table` height · TypeScript · `tsx` 脚本单测
+
+**Spec:** `docs/superpowers/specs/2026-09-01-web-admin-list-table-viewport-scroll-design.md`
+
+**Demo:** `docs/list-table-scroll-demo.html`
+
+---
+
+## File map
+
+| File | Responsibility |
+|------|----------------|
+| `web-admin/src/composables/listTableViewportHeight.ts` | 纯函数:高度公式 + 默认常量 |
+| `web-admin/scripts/check-list-table-viewport-height.ts` | 无浏览器依赖的公式断言(tsx) |
+| `web-admin/src/composables/useListTableViewportHeight.ts` | 测量 DOM、resize/观察、返回 `tableHeight` + `recalc` |
+| `web-admin/src/views/demo/unit/index.vue` | 模板样板页接入 |
+| `web-admin/src/views/bx/reimburse/apply/index.vue` | ListTable + wrap 样板接入 |
+| `web-admin/src/styles/bx/index.scss` | 澄清 `.table-scroll` 与 EP height 模式不双滚 |
+| `docs/web-admin-ui-spec-fresh-mint.md` §7.6 | 规范改为视口填满策略 |
+| `docs/list-table-scroll-demo.html` | Demo 文案对齐「分页下 10px」 |
+| `openspec/.../ui-conventions/spec.md` | 新增 SHALL |
+| 后续批处理列表页 | 同构接入(Task 6 清单) |
+
+---
+
+### Task 1: 纯函数高度公式 + 脚本单测
+
+**Files:**
+- Create: `web-admin/src/composables/listTableViewportHeight.ts`
+- Create: `web-admin/scripts/check-list-table-viewport-height.ts`
+
+- [ ] **Step 1: 写失败断言脚本(文件尚不存在时应无法通过)**
+
+创建 `web-admin/scripts/check-list-table-viewport-height.ts`:
+
+```ts
+import assert from 'node:assert/strict'
+import {
+ LIST_TABLE_VIEWPORT_BOTTOM_GAP,
+ LIST_TABLE_VIEWPORT_MIN_HEIGHT,
+ calcListTableViewportHeight
+} from '../src/composables/listTableViewportHeight'
+
+assert.equal(LIST_TABLE_VIEWPORT_BOTTOM_GAP, 10)
+assert.equal(LIST_TABLE_VIEWPORT_MIN_HEIGHT, 240)
+
+// contentBottom=1000, tableTop=200, pager=48 → 1000-200-48-10 = 742
+assert.equal(
+ calcListTableViewportHeight({
+ contentViewportBottom: 1000,
+ tableTop: 200,
+ pagerHeight: 48
+ }),
+ 742
+)
+
+// 无分页
+assert.equal(
+ calcListTableViewportHeight({
+ contentViewportBottom: 800,
+ tableTop: 100,
+ pagerHeight: 0
+ }),
+ 690
+)
+
+// 过矮时夹到 minHeight
+assert.equal(
+ calcListTableViewportHeight({
+ contentViewportBottom: 400,
+ tableTop: 300,
+ pagerHeight: 48,
+ minHeight: 240,
+ bottomGap: 10
+ }),
+ 240
+)
+
+// 自定义 min / gap
+assert.equal(
+ calcListTableViewportHeight({
+ contentViewportBottom: 1000,
+ tableTop: 100,
+ pagerHeight: 40,
+ minHeight: 100,
+ bottomGap: 16
+ }),
+ 844
+)
+
+console.log('check-list-table-viewport-height: ok')
+```
+
+- [ ] **Step 2: 跑脚本确认失败**
+
+Run:
+
+```bash
+cd web-admin && pnpm exec tsx scripts/check-list-table-viewport-height.ts
+```
+
+Expected: 失败(模块不存在 / 无法解析)
+
+- [ ] **Step 3: 实现纯函数**
+
+创建 `web-admin/src/composables/listTableViewportHeight.ts`:
+
+```ts
+/** 分页底边到内容区底的默认空隙(px) */
+export const LIST_TABLE_VIEWPORT_BOTTOM_GAP = 10
+
+/** 表体动态高度下限(px),避免筛选展开时表被压没 */
+export const LIST_TABLE_VIEWPORT_MIN_HEIGHT = 240
+
+export type CalcListTableViewportHeightInput = {
+ /** 主内容滚动容器可视底边(如 #app-content.getBoundingClientRect().bottom) */
+ contentViewportBottom: number
+ /** 表格锚点顶边(同坐标系,通常 getBoundingClientRect().top) */
+ tableTop: number
+ /** 分页条实际高度;无分页传 0 */
+ pagerHeight: number
+ minHeight?: number
+ bottomGap?: number
+}
+
+/**
+ * 计算列表表体应设置的 height,使「表 + 分页」落在内容视口内,且分页下方留 bottomGap。
+ * tableHeight = floor(contentBottom - tableTop - pagerHeight - bottomGap),再与 minHeight 取大。
+ */
+export function calcListTableViewportHeight(
+ input: CalcListTableViewportHeightInput
+): number {
+ const minHeight = input.minHeight ?? LIST_TABLE_VIEWPORT_MIN_HEIGHT
+ const bottomGap = input.bottomGap ?? LIST_TABLE_VIEWPORT_BOTTOM_GAP
+ const raw = Math.floor(
+ input.contentViewportBottom - input.tableTop - input.pagerHeight - bottomGap
+ )
+ if (!Number.isFinite(raw)) return minHeight
+ return Math.max(raw, minHeight)
+}
+```
+
+- [ ] **Step 4: 再跑脚本确认通过**
+
+Run:
+
+```bash
+cd web-admin && pnpm exec tsx scripts/check-list-table-viewport-height.ts
+```
+
+Expected: 打印 `check-list-table-viewport-height: ok`
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add web-admin/src/composables/listTableViewportHeight.ts \
+ web-admin/scripts/check-list-table-viewport-height.ts
+git commit -m "$(cat <<'EOF'
+feat(web-admin): add list table viewport height formula
+
+Pure calc keeps pagination visible with a 10px bottom gap for main list scroll UX.
+EOF
+)"
+```
+
+---
+
+### Task 2: composable `useListTableViewportHeight`
+
+**Files:**
+- Create: `web-admin/src/composables/useListTableViewportHeight.ts`
+
+- [ ] **Step 1: 实现 composable**
+
+创建 `web-admin/src/composables/useListTableViewportHeight.ts`:
+
+```ts
+import {
+ nextTick,
+ onBeforeUnmount,
+ onMounted,
+ ref,
+ unref,
+ watch,
+ type Ref
+} from 'vue'
+import { useEventListener, useResizeObserver } from '@vueuse/core'
+import {
+ LIST_TABLE_VIEWPORT_BOTTOM_GAP,
+ LIST_TABLE_VIEWPORT_MIN_HEIGHT,
+ calcListTableViewportHeight
+} from '@/composables/listTableViewportHeight'
+
+export type UseListTableViewportHeightOptions = {
+ /** 表格锚点(包住 ListTable/el-table 的元素,不含分页) */
+ tableEl: Ref