docs(skill): 多形态章节改写为默认数据流与逃生口语义

This commit is contained in:
augushong
2026-09-14 19:04:53 +00:00
parent 994803f155
commit 765805ec0c

View File

@@ -1,6 +1,6 @@
---
name: "ulthon-admin-table"
description: "后台列表页表格机制与定制完整指南:核心机制(ua 命名空间、三件套)、字段配置、内置渲染器、按钮系统(含页面分类标准 A/B/C/D)、搜索系统(前端配置 + 后端 buildTableParames)、后端配套、常见模式与排错、多形态渲染(受控模式 controlled、layoutList 五级模板协议 tplFunction/tplLay/tplVue/tplLayId/tplVueId、卡片形态、渲染上下文)。需要业务定制 CURD 生成的列表页(加按钮、改字段、调搜索)、排查按钮/搜索不生效、或自定义卡片/模板形态时调用。"
description: "后台列表页表格机制与定制完整指南:核心机制(ua 命名空间、三件套)、字段配置、内置渲染器、按钮系统(含页面分类标准 A/B/C/D)、搜索系统(前端配置 + 后端 buildTableParames)、后端配套、常见模式与排错、多形态渲染(受控默认数据流与 controlled:false 逃生口、layoutList 五级模板协议 tplFunction/tplLay/tplVue/tplLayId/tplVueId、双端自动注入、按端 layoutDefault、卡片形态、渲染上下文)。需要业务定制 CURD 生成的列表页(加按钮、改字段、调搜索)、排查按钮/搜索不生效、自定义卡片/模板形态、或判断页面是否需要 controlled:false 逃生口时调用。"
---
# 后台表格机制与定制(ulthon-admin-table)
@@ -16,7 +16,8 @@ CURD 生成器生成列表页骨架后,所有业务定制(按钮、字段渲
- 自定义字段渲染(图片、状态、关联数据)
- 实现状态流转按钮(带 `_if` 显隐)
- 跨控制器跳转按钮
- 列表自定义卡片/模板形态(`controlled` + `layoutList`,见第十一章)
- 列表自定义卡片/模板形态与表格/卡片切换(`layoutList` + `layoutDefault`,见第十一章)
- 判断页面走的是哪种数据流,或是否需要 `controlled: false` 逃生口(见第十一章)
## 一、核心机制总览
@@ -26,8 +27,8 @@ CURD 生成器生成列表页骨架后,所有业务定制(按钮、字段渲
| 文件 | 作用 |
|------|------|
| `admin-table.js` | 表格核心:render / formatCols / renderOperat / 内置渲染器 / 搜索监听 / 受控模式接线与多形态渲染(reloadData shim、五级模板协议、渲染上下文) |
| `admin-table-store.js` | 受控模式数据层(全局 `window.UA_TABLE_STORE`):dataProvider / pager / selection / reload 四模块 |
| `admin-table.js` | 表格核心:render / formatCols / renderOperat / 内置渲染器 / 搜索监听 / 受控数据流接线(默认数据流与 controlled:false 逃生口分流)与多形态渲染(reloadData shim、双端形态注入、五级模板协议、渲染上下文) |
| `admin-table-store.js` | 受控数据流数据层(全局 `window.UA_TABLE_STORE`,所有 `ua.table.render` 页面的默认数据层):dataProvider / pager / selection / reload 四模块 |
| `admin-core.js` | ua 全局组装、headers、url、parame 等工具 |
| `admin-utils.js` | `ua.getDataBrage`、`ua.checkMobile`、`ua.getPageSetting` 等工具 |
| `admin-listen.js` | 事件监听(`ua.listen`) |
@@ -618,7 +619,7 @@ CURD 生成时会将关联模型的全部字段(如 `systemAdmin.*`)输出
## 十、实测代码引用表
每个特性都标注实测来源(文件:行),若代码与本文档冲突,以代码为准。
每个特性标注实测来源(多为文件:行;受控/多形态相关特性因行号随迭代漂移,改用 grep 关键词定位),若代码与本文档冲突,以代码为准。
| 特性 | 实测来源 |
|------|---------|
@@ -652,36 +653,40 @@ CURD 生成时会将关联模型的全部字段(如 `systemAdmin.*`)输出
| 实际示例:标准 CRUD | `extend/base/admin/view/system/admin/index.js` |
| 实际示例:`_if` 条件按钮 | `app/admin/view/system/timer_config/index.js` |
| 实际示例:自定义 templet | `app/admin/view/system/timer_log/index.js` |
| `controlled` 受控分支接线 | `admin-table.js:914`(注册 store、自建分页与模板容器、首屏取数) |
| reloadData shim(受控分流/透传) | `admin-table.js:21` |
| `ua.table.reloadData` / `ua.table.checkStatus` 分发入口 | `admin-table.js:373` / `:379` |
| store 五入口(register/has/reload/fetch/checkStatus) | `admin-table-store.js:531` |
| layoutList 规范化(字符串/对象条目) | `admin-table.js:69`(normalizeLayoutList) |
| 五级钩子优先级解析 | `admin-table.js:194`(resolveLayoutEngine) |
| 内置 card 模板(tplFunction) | `admin-table.js:112`(builtinCardTemplate) |
| 渲染上下文构建器 | `admin-table.js:394`(buildLayoutContext) |
| tplFunction/laytpl/Vue 调用约定注释 | `admin-table.js:466-508` |
| renderLayoutForm(模板管线入口) | `admin-table.js:511` |
| 统一行选中委托(data-table-id + data-id) | `admin-table.js:158` |
| getLayoutForm / setLayoutForm | `admin-table.js:670` / `:678` |
| 默认形态解析(layoutDefault/checkMobile/记忆) | `admin-table.js:943` |
| 视图切换按钮注入(条目 > 1) | `admin-table.js:732`,记忆写入 `:2025` |
| `ua.checkMobile` 判定 | `admin-utils.js:6`,实现 `public/static/common/js/app.js:63` |
| 实际示例:名片形态(Vue 模板) | `extend/base/admin/view/system/admin/index.html:19` + `index.js:10` |
| 受控默认数据流 / 逃生口分叉 | `grep "controlled !== false" public/static/plugs/ulthon-admin/admin-table.js`(注入门控、数据流分叉、store 接线共三处) |
| 受控接线(注册 store、自建分页与模板容器、首屏取数) | `grep "受控接线" admin-table.js` |
| 逃生口分支(引擎 parseData 装配保留) | `grep "逃生口" admin-table.js` |
| reloadData shim(受控分流/透传) | `grep "__uaOriginalReloadData" admin-table.js` |
| `ua.table.reloadData` / `ua.table.checkStatus` 分发入口 | `grep "受控/非受控分发入口" admin-table.js` |
| store 五入口(register/has/reload/fetch/checkStatus) | `grep "五个主入口" public/static/plugs/ulthon-admin/admin-table-store.js` |
| layoutList 规范化(字符串/对象条目) | `grep "normalizeLayoutList" admin-table.js` |
| 双端自动注入与视图按钮(未配 layoutList 注入 `['table','card']`) | `grep "autoInjected" admin-table.js` |
| 显式 defaultToolbar 时视图按钮补投(去重) | `grep "去重追加" admin-table.js` |
| 按端默认形态解析(记忆 > 按端显式 > 端缺省三层链) | `grep "defaultLayoutName" admin-table.js` |
| 形态记忆读/写(`table-layout-default`) | `grep "table-layout-default" admin-table.js` |
| 五级钩子优先级解析 | `grep "resolveLayoutEngine" admin-table.js` |
| 内置 card 模板(tplFunction) | `grep "builtinCardTemplate" admin-table.js` |
| 渲染上下文构建器 | `grep "buildLayoutContext" admin-table.js` |
| tplFunction/laytpl/Vue 调用约定注释 | `grep "列表级单实例" admin-table.js` |
| renderLayoutForm(模板管线入口) | `grep "renderLayoutForm: function" admin-table.js` |
| 统一行选中委托(data-table-id + data-id) | `grep "统一行选中委托" admin-table.js` |
| getLayoutForm / setLayoutForm | `grep "getLayoutForm" admin-table.js` |
| `ua.checkMobile` 判定 | `grep "checkMobile" public/static/common/js/app.js`(`admin-utils.js` 转发) |
| 实际示例:三形态 + 按端默认 | `extend/base/admin/view/system/admin/index.js`(render 配置块:layoutList 三形态 + layoutDefault 对象式)+ `index.html`(usercardTpl 模板节点) |
## 十一、多形态渲染(受控模式与自定义模板)
## 十一、多形态渲染(受控数据流与自定义模板)
列表除了引擎表格形态,还能以卡片等自定义模板形态渲染。模板形态运行在受控模式之上:数据由框架数据层接管,模板只负责"一行/一屏数据长什么样"。官方完整范例是用户管理的名片形态(`extend/base/admin/view/system/admin/index.html:19` 的 usercardTpl 节点 + `extend/base/admin/view/system/admin/index.js:10` 的 layoutList 配置)。
`ua.table.render` 页面默认运行在受控数据流上,并默认获得表格/卡片两种形态,两者都是零配置即得。开发者只需要回答两个问题:想定制形态怎么配(`layoutList` + `layoutDefault`,见 11.2),以及自己的页面是否属于少数需要逃生口的场景(`controlled: false`,见 11.1)。官方完整范例是用户管理页:`extend/base/admin/view/system/admin/index.js` 顶部 render 配置里的 layoutList 三形态与 layoutDefault 对象式按端默认,建议直接对照源码。
### 11.1 受控模式
### 11.1 受控数据流(默认)与逃生口
`ua.table.render` 的 options 加 `controlled: true` 即开启。受控后页面数据由 `admin-table-store.js`(全局 `window.UA_TABLE_STORE`)接管:引擎不再自己发请求,首屏、搜索、排序、翻页、switch 回滚、删除刷新等所有数据重载统一走 reloadData 的 shim 分流进 store。
**受控是默认数据流**:`ua.table.render` 不传 `controlled` 即受控,页面数据由 `admin-table-store.js`(全局 `window.UA_TABLE_STORE`)接管,引擎只做纯渲染,不再自己发请求。首屏、搜索、排序、翻页、switch 回滚、删除刷新等所有数据重载统一走 reloadData 的 shim 分流进 store。
| 组件 | 说明 |
|------|------|
| shim(`table.reloadData` 包装) | 受控表(store 已注册该 id)→ `store.reload`;非受控表原样透传引擎(admin-table.js:21) |
| `ua.table.reloadData(id, opts)` | 页面代码刷新表格的推荐入口:按受控与否自动分流,受控/非受控页面通吃(admin-table.js:373) |
| `ua.table.checkStatus(id)` | 取选中行的推荐入口,同样自动分流(admin-table.js:379) |
| shim(`table.reloadData` 包装) | 受控表(store 已注册该 id)→ `store.reload`;非受控表原样透传引擎 |
| `ua.table.reloadData(id, opts)` | 页面代码刷新表格的推荐入口:按受控与否自动分流,受控/非受控页面通吃 |
| `ua.table.checkStatus(id)` | 取选中行的推荐入口,同样自动分流 |
旧页面里直呼 `layui.table.reloadData` / `layui.table.checkStatus` 的代码仍会被 shim 覆盖到,但新代码请统一用 `ua.table.reloadData` / `ua.table.checkStatus`。
@@ -694,11 +699,24 @@ store 内部四个模块(admin-table-store.js):
| `selection` | 选中集合:跨页保持勾选,与引擎勾选是同一事实源 |
| `reload` | 重载编排:合并 where/page 后 fetch,成功回调里按当前形态分发渲染 |
对外五个主入口 `register / has / reload / fetch / checkStatus`(admin-table-store.js:531)。业务页面一般不直接调 store,用上表两个 `ua.table.*` 分发入口就够了。
对外五个主入口 `register / has / reload / fetch / checkStatus`。业务页面一般不直接调 store,用上表两个 `ua.table.*` 分发入口就够了。
受控表在引擎视图与分页条之间有一个模板渲染容器 `#ua-layout-{tableId}`,模板形态的内容渲染在这里,与引擎数据区互斥显隐;工具栏与搜索区两形态共用(卡片形态下新增/批量删除/搜索照常可用)。
**受控页的模板渲染容器**:受控表在引擎视图与分页条之间有一个模板形态容器,元素 id 为 `ua-layout-{tableRenderId}`(默认页面即 `#ua-layout-currentTableRenderId`,取的是 render 实例 id,不是表格元素 id,写选择器时勿混淆)。模板形态的内容渲染在这里,与引擎数据区互斥显隐;工具栏与搜索区两形态共用(卡片形态下新增/批量删除/搜索照常可用)。
### 11.2 五级模板协议(layoutList)
**逃生口 `controlled: false`**:受控数据流覆盖绝大多数页面,少数下游页面依赖引擎 url 模式的行为,典型场景是自定义了 `parseData` 改写响应结构、或直接消费引擎请求管线。这类页面显式传 `controlled: false` 退回引擎 url 模式:引擎自己取数与分页,页面自定义 parseData 与选择态注入照常生效。注意逃回的代价:不注入形态清单、工具栏没有视图切换按钮、恒为表格形态,多形态渲染整体不可用;`ua.table.reloadData` 等分发入口对其透传引擎。
```js
// 逃生口示例:仅依赖引擎 url 模式行为的页面才加这一行
ua.table.render({
init: init,
controlled: false, // 退回引擎 url 模式,放弃多形态
cols: [[/* ... */]],
});
```
### 11.2 形态清单(layoutList)与双端自动注入
**双端自动注入**:受控页不配置 `layoutList` 时,框架双端自动注入 `['table', 'card']`,条目数大于 1,双端工具栏自动出现「视图」切换按钮。显式配置了 `layoutList` 的页面保持显式配置优先,不注入。`layoutList: ['table']` 单条目即锁定表格出口:无视图按钮,不可切形态。
`layoutList` 声明表格支持的形态清单,条目有两种写法:
@@ -725,11 +743,28 @@ layoutList: ['table', {name: 'usercard', title: '名片', tplVueId: '#usercardTp
- 内置 `card` 本质是一个框架内置的 tplFunction 条目(header/body/footer 卡片骨架,样式类 `.ua-data-card` 族);条目自带任意钩子时完全覆盖内置实现
- 模板渲染失败(节点缺失、laytpl 错误、Vue 未引入或挂载失败)自动 console.error 并回退表格形态,不白屏;回退不写记忆,修复后可再切换
**默认形态与视图切换**:
**按端默认形态(layoutDefault)**:推荐对象式写法,按端声明默认形态。
- 默认 `table`。解析优先级:localStorage 按页记忆 > 显式 `layoutDefault` 或手机端自动 card > `table`(admin-table.js:943)
- `layoutList` 条目数 > 1 时,工具栏自动注入「视图」按钮;点击下拉条目 = 写记忆 + 切形态,切换用的是 store 当页数据,零网络请求
- 记忆键 `table-layout-default`(经 `ua.getPageSetting` 按页加前缀存储),运行期 API:`ua.table.getLayoutForm(id)` 读当前形态、`ua.table.setLayoutForm(id, name)` 切形态
```js
layoutDefault: {pc: 'table', mobile: 'card'}, // 官方示例页写法:按端取值
layoutDefault: 'card', // 旧写法兼容:字符串,双端同值
layoutDefault: true, // 旧协议"自动判断",视为未配置走端缺省
```
默认形态按三层链解析(优先级从高到低),端判定用 `ua.checkMobile()`:
1. **记忆**:localStorage 按页记忆,键 `table-layout-default`(经 `ua.getPageSetting`/`ua.setPageSetting` 带页面键前缀存储);值不在形态清单内忽略
2. **按端显式**:`layoutDefault` 按当前端取值,对象式 `pc`/`mobile` 键缺省视为该端未配置;取值不在清单内忽略
3. **端缺省**:PC 取 `table`,无则清单首条目;mobile 取 `card`,无则 `table`,再无则首条目
形态清单为空时恒为 `table`:非受控逃生口页,或 `layoutList` 配置了但所有条目都无效(垃圾清单),都不会在空清单上取首条目。
**视图切换与 CURD 缺省**:
- `layoutList` 条目数 > 1 时工具栏出现「视图」按钮;点击下拉条目即写记忆并切形态,切换用的是 store 当页数据,零网络请求
- 页面显式配置了 `defaultToolbar` 时,视图按钮会自动补投进最终工具栏(自动注入场景下去重追加),普通开发者无需关心
- 运行期 API:`ua.table.getLayoutForm(id)` 读当前形态、`ua.table.setLayoutForm(id, name)` 切形态
- **CURD 页面缺省即得双形态**:CURD 生成的列表页不含任何受控/形态配置,自动获得受控数据流 + 双端「表格/卡片」+ 视图按钮,模板零改动;PC 端缺省表格、手机端缺省卡片
**两条硬约束**:
@@ -795,19 +830,19 @@ layoutList: ['table', {name: 'mycard', title: '自定义卡', tplVueId: '#myVueT
- 行根元素带 `data-table-id` + `data-id` 两属性即获得统一行选中委托:点卡片切换勾选、选中态经 `:class` 绑 `ua-data-card-checked` 高亮回显;按钮/表单控件自动豁免不误触,零 JS
- 事件无需自己绑定:操作按钮(data-open/data-request 等属性)与 switch/checkbox 经页面级委托与 form 事件天然生效;`@click` 可用,但处理函数内不要修改模板数据(渲染上下文被冻结,数据更新一律整卡销毁重建)
**完整官方范例**:用户管理名片卡片,接入面只有两处,建议直接对照源码:
**完整官方范例**:用户管理三形态(表格 + 名片 + 内置卡片),建议直接对照源码:
| 位置 | 内容 |
|------|------|
| `extend/base/admin/view/system/admin/index.html:19` | usercardTpl 模板节点 + 页面级网格样式 |
| `extend/base/admin/view/system/admin/index.js:10` | layoutList 一行接入(`controlled: true` 在第 5 行) |
| `extend/base/admin/view/system/admin/index.js` | render 配置块:layoutList 三形态 + layoutDefault 对象式按端默认(`{pc: 'table', mobile: 'card'}`) |
| `extend/base/admin/view/system/admin/index.html` | usercardTpl 模板节点(`<script type="text/x-template">`,位于渲染容器之外)+ 页面级网格样式 |
该范例演示了三种典型取值通道:渲染器列(status)用 `_cells` + v-html 承接框架语义、纯文本列(username/create_time/phone)直接原始字段插值、头像(head_img)因名片需要圆形大图绕开渲染器直接绑原始字段。模板形态的网格布局由模板自带容器承担(Vue 形态不带内置卡片网格类)。
### 11.5 手机端默认行为
### 11.5 双端默认行为
- 手机端判定 `ua.checkMobile()`(admin-utils.js:6,实现 public/static/common/js/app.js:63):UA 关键字(Android/iPhone/iPad 等)或 `window.screen` 宽 < 600 且高 < 800。判的是物理屏幕指标,**浏览器开发者工具仅改视口宽度不会触发**;设备仿真验证手机默认形态时,必须同时覆写 UA 或仿真 screen 尺寸,仅设 viewport 无效
- `layoutList` 含 `card` 条目时,手机端默认进入 card 形态;localStorage 记忆值优先级更高(记忆值不在 layoutList 内时忽略)
- 双端自动注入后 PC 端同样有视图切换按钮与卡片形态,端差异只体现在缺省形态上:PC 缺省 `table`,手机缺省 `card`(记忆与按端 layoutDefault 的优先级更高,见 11.2 解析链)
- 手机端判定 `ua.checkMobile()`(`admin-utils.js` 转发,实现位于 `public/static/common/js/app.js`):UA 关键字(Android/iPhone/iPad 等)或 `window.screen` 宽 < 600 且高 < 800。判的是物理屏幕指标,**浏览器开发者工具仅改视口宽度不会触发**;设备仿真验证手机默认形态时,必须同时覆写 UA 或仿真 screen 尺寸,仅设 viewport 无效
- 卡片形态能力边界:工具栏(刷新/新增/批量删除/导出)与搜索区两形态共用、照常可用;勾选语义由卡片勾位图标与选中委托承接(checkbox 列本身不进模板上下文);`_disabled` 行整元素不可勾选
## 相关技能