diff --git a/.agents/skills/ulthon-admin-table/SKILL.md b/.agents/skills/ulthon-admin-table/SKILL.md
index 19570a6..485a847 100644
--- a/.agents/skills/ulthon-admin-table/SKILL.md
+++ b/.agents/skills/ulthon-admin-table/SKILL.md
@@ -1,6 +1,6 @@
---
name: "ulthon-admin-table"
-description: "后台列表页表格机制与定制完整指南:核心机制(ua 命名空间、三件套)、字段配置、内置渲染器、按钮系统(含页面分类标准 A/B/C/D)、搜索系统(前端配置 + 后端 buildTableParames)、后端配套、常见模式与排错、多形态渲染(受控默认数据流与 controlled:false 逃生口、layoutList 五级模板协议 tplFunction/tplLay/tplVue/tplLayId/tplVueId、双端自动注入、按端 layoutDefault、卡片形态、渲染上下文)。需要业务定制 CURD 生成的列表页(加按钮、改字段、调搜索)、排查按钮/搜索不生效、自定义卡片/模板形态、或判断页面是否需要 controlled:false 逃生口时调用。"
+description: "后台列表页表格机制与定制完整指南:核心机制(ua 命名空间、三件套)、字段配置、内置渲染器、按钮系统(含页面分类标准 A/B/C/D)、搜索系统(前端配置 + 后端 buildTableParames)、后端配套、常见模式与排错、多形态渲染机制(受控默认数据流与 controlled:false 逃生口、layoutList 五级模板协议 tplFunction/tplLay/tplVue/tplLayId/tplVueId、双端自动注入、layoutDefault 按端默认与记忆链、卡片形态)。需要业务定制 CURD 生成的列表页(加按钮、改字段、调搜索)、排查按钮/搜索不生效、配置 layoutList/layoutDefault/视图切换,或判断页面是否需要 controlled:false 逃生口时调用;自定义卡片模板的创作(上下文契约、实战配方、设计模式、坑与验收)见 ulthon-card-template 技能。"
---
# 后台表格机制与定制(ulthon-admin-table)
@@ -16,7 +16,7 @@ CURD 生成器生成列表页骨架后,所有业务定制(按钮、字段渲
- 自定义字段渲染(图片、状态、关联数据)
- 实现状态流转按钮(带 `_if` 显隐)
- 跨控制器跳转按钮
-- 列表自定义卡片/模板形态与表格/卡片切换(`layoutList` + `layoutDefault`,见第十一章)
+- 列表多形态机制与表格/卡片切换(`layoutList` + `layoutDefault`,见第十一章;自定义卡片模板的创作见 [ulthon-card-template](./ulthon-card-template/SKILL.md) 技能)
- 判断页面走的是哪种数据流,或是否需要 `controlled: false` 逃生口(见第十一章)
## 一、核心机制总览
@@ -219,7 +219,7 @@ var init = {
### 5.1 顶部工具栏(toolbar)
-通过 `ua.table.render({ toolbar: [...] })` 配置。**不声明时默认 `['refresh', 'add', 'delete', 'export']`**——这意味着只读页面如果不覆盖,会显示无用的"新增/删除"按钮(见 9.2 常见错误)。
+通过 `ua.table.render({ toolbar: [...] })` 配置。**不声明时默认 `['refresh', 'add', 'delete', 'export']`**,这意味着只读页面如果不覆盖,会显示无用的"新增/删除"按钮(见 9.2 常见错误)。
**字符串简写**(自动检查权限):
@@ -256,7 +256,7 @@ ua.table.render({
### 5.2 行级操作按钮(operat)
-在 `cols` 最后一列配置 `templet: ua.table.tool` + `operat` 数组。**不声明 operat 时,tool 函数内自动填充 `['edit', 'delete']`**(admin-table.js:1055)。
+在 `cols` 最后一列配置 `templet: ua.table.tool` + `operat` 数组。**不声明 operat 时,tool 函数内自动填充 `['edit', 'delete']`**。
```js
{
@@ -393,7 +393,7 @@ operat: [
]
```
-每个 `[]` 内的对象是独立按钮,按顺序渲染。`renderOperat`(admin-table.js:769)做权限整列裁剪:只要任意一个按钮组内有按钮有权限,整列就保留。
+每个 `[]` 内的对象是独立按钮,按顺序渲染。`renderOperat`(定位见第十章速查表)做权限整列裁剪:只要任意一个按钮组内有按钮有权限,整列就保留。
## 六、页面分类标准(A/B/C/D)
@@ -619,37 +619,36 @@ CURD 生成时会将关联模型的全部字段(如 `systemAdmin.*`)输出
## 十、实测代码引用表
-每个特性标注实测来源(多为文件:行;受控/多形态相关特性因行号随迭代漂移,改用 grep 关键词定位),若代码与本文档冲突,以代码为准。
+每个特性标注实测来源(文件 + grep 关键词定位;行号随迭代漂移,故不逐条标注行号),若代码与本文档冲突,以代码为准。
| 特性 | 实测来源 |
|------|---------|
-| `ua.table.render` 入口 | `public/static/plugs/ulthon-admin/admin-table.js:14` |
-| `formatCols`(cols 加工) | `admin-table.js:921` |
-| `renderOperat`(操作列整列裁剪) | `admin-table.js:769` |
-| `renderToolbar`(顶部工具栏) | `admin-table.js:251` |
-| `renderSearch`(搜索表单生成) | `admin-table.js:294` |
-| `search` 默认 `true` | `admin-table.js:307`(`ua.parame(d.search, true)`) |
-| `searchOp` 默认 `'%*%'` | `admin-table.js:312` |
-| `search: 'number_limit'` / `'time_limit'` | `admin-table.js:348-369` |
-| `listenTableSearch`(filter+op 构造) | `admin-table.js:1334` |
-| `tool` 函数(自动填充默认 operat) | `admin-table.js:1053`,自动填充在 1055 |
-| `tool` 内置 'edit' 处理 | `admin-table.js:1061` |
-| `tool` 内置 'delete' 处理 | `admin-table.js:1082` |
-| `operat._if` 显隐判定 | `admin-table.js:1113-1124` |
-| `operat.auth` 默认 `'add'` | `admin-table.js:1105` |
-| `list` 渲染器 | `admin-table.js:1169` |
-| `filePreview` 渲染器 | `admin-table.js:1180` |
-| `image` 渲染器(imageSplit/imageJoin) | `admin-table.js:1195-1213` |
-| `url` 渲染器(urlNameField) | `admin-table.js:1215` |
-| `switch` 渲染器(自动调 modify) | `admin-table.js:1245`,modify 调用在 `listenSwitch:1365` |
-| `date` 渲染器 | `admin-table.js:1281` |
-| `ua.getDataBrage` 实现 | `public/static/plugs/ulthon-admin/admin-utils.js:63-74` |
+| `ua.table.render` 入口 | `public/static/plugs/ulthon-admin/admin-table.js`(`grep "render: function (options)"`) |
+| `formatCols`(cols 加工) | `admin-table.js`(`grep "formatCols: function"`) |
+| `renderOperat`(操作列整列裁剪) | `admin-table.js`(`grep "renderOperat(data, elem)"`) |
+| `renderToolbar`(顶部工具栏) | `admin-table.js`(`grep "renderToolbar: function"`) |
+| `renderSearch`(搜索表单生成) | `admin-table.js`(`grep "renderSearch: function"`) |
+| `search` 默认 `true` | `admin-table.js`(`grep "d.search, true"`) |
+| `searchOp` 默认 `'%*%'` | `admin-table.js`(`grep "d.searchOp"`) |
+| `search: 'number_limit'` / `'time_limit'` | `admin-table.js`(`grep "number_limit"`) |
+| `listenTableSearch`(filter+op 构造) | `admin-table.js`(`grep "listenTableSearch: function"`) |
+| `tool` 函数(自动填充默认 operat) | `admin-table.js`(`grep "operat = option.operat"`,tool 函数内) |
+| `tool` 内置 'edit' / 'delete' 处理 | 同上 tool 函数内 `case 'edit'` / `case 'delete'` 分支 |
+| `operat._if` 显隐判定 | `admin-table.js`(`grep "operat._if"`) |
+| `operat.auth` 默认 `'add'` | `admin-table.js`(`grep "operat.auth = operat.auth \|\|"`) |
+| `list` 渲染器 | `admin-table.js`(`grep "list: function"`) |
+| `filePreview` 渲染器 | `admin-table.js`(`grep "filePreview: function"`) |
+| `image` 渲染器(imageSplit/imageJoin) | `admin-table.js`(`grep "image: function"`) |
+| `url` 渲染器(urlNameField) | `admin-table.js`(`grep "url: function"`) |
+| `switch` 渲染器(自动调 modify) | `admin-table.js`(`grep "switch: function"`),modify 调用在 `grep "listenSwitch: function"` |
+| `date` 渲染器 | `admin-table.js`(`grep "date: function"`) |
+| `ua.getDataBrage` 实现 | `admin-utils.js`(`grep "ua.getDataBrage = function"`) |
| `#data-brage` 隐藏元素 | `extend/base/admin/view/layout/default.html`(layout 文件) |
-| `data_brage` assign | `extend/base/common/controller/AdminControllerBase.php:273` |
-| `buildTableParames`(filter+op 解析) | `extend/base/common/controller/AdminControllerBase.php:405` |
-| `$relationSearch` 属性 | `AdminControllerBase.php:69`(默认 false) |
-| `$selectWhere` 属性 | `AdminControllerBase.php:63` |
-| `selectList()` 方法 | `AdminControllerBase.php:489` |
+| `data_brage` assign | `AdminControllerBase.php`(`grep "assign('data_brage'"`) |
+| `buildTableParames`(filter+op 解析) | `AdminControllerBase.php`(`grep "function buildTableParames"`) |
+| `$relationSearch` 属性 | `AdminControllerBase.php`(`grep "protected $relationSearch"`,默认 false) |
+| `$selectWhere` 属性 | `AdminControllerBase.php`(`grep "protected $selectWhere"`) |
+| `selectList()` 方法 | `AdminControllerBase.php`(`grep "public function selectList"`) |
| 实际示例:标准 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` |
@@ -671,12 +670,13 @@ CURD 生成时会将关联模型的全部字段(如 `systemAdmin.*`)输出
| renderLayoutForm(模板管线入口) | `grep "renderLayoutForm: function" admin-table.js` |
| 统一行选中委托(data-table-id + data-id) | `grep "统一行选中委托" admin-table.js` |
| getLayoutForm / setLayoutForm | `grep "getLayoutForm" admin-table.js` |
+| 内置卡片公共样式(`.ua-data-card` 族) | `grep "ua-data-card" public/static/admin/css/public.css` |
| `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.js`(render 配置块:layoutList 三形态 + layoutDefault 对象式)+ `index.html`(usercardTpl 模板节点);第二官方范例 `extend/base/admin/view/mall/goods/`(goodscardTpl + `layoutDefault: 'goodscard'` 字符串写法) |
-## 十一、多形态渲染(受控数据流与自定义模板)
+## 十一、多形态渲染机制(受控数据流与形态协议)
-`ua.table.render` 页面默认运行在受控数据流上,并默认获得表格/卡片两种形态,两者都是零配置即得。开发者只需要回答两个问题:想定制形态怎么配(`layoutList` + `layoutDefault`,见 11.2),以及自己的页面是否属于少数需要逃生口的场景(`controlled: false`,见 11.1)。官方完整范例是用户管理页:`extend/base/admin/view/system/admin/index.js` 顶部 render 配置里的 layoutList 三形态与 layoutDefault 对象式按端默认,建议直接对照源码。
+`ua.table.render` 页面默认运行在受控数据流上,并默认获得表格/卡片两种形态,两者都是零配置即得。本章只讲机制:数据流与逃生口(11.1)、形态清单协议与解析链(11.2)、双端默认行为(11.3)。要为自己的页面定制专属卡片模板(适用判断、上下文契约、实战配方、设计模式库、坑与验收),见 [ulthon-card-template](./ulthon-card-template/SKILL.md) 技能。
### 11.1 受控数据流(默认)与逃生口
@@ -763,92 +763,30 @@ layoutDefault: true, // 旧协议"自动判断",视
- `layoutList` 条目数 > 1 时工具栏出现「视图」按钮;点击下拉条目即写记忆并切形态,切换用的是 store 当页数据,零网络请求
- 页面显式配置了 `defaultToolbar` 时,视图按钮会自动补投进最终工具栏(自动注入场景下去重追加),普通开发者无需关心
-- 运行期 API:`ua.table.getLayoutForm(id)` 读当前形态、`ua.table.setLayoutForm(id, name)` 切形态
+- 运行期 API:`ua.table.getLayoutForm(id)` 读当前形态、`ua.table.setLayoutForm(id, name)` 切形态。API 切换**不写记忆**,只有点视图下拉条目才写 `ua.setPageSetting`;API 切换后刷新会回到记忆形态,这是设计行为
- **CURD 页面缺省即得双形态**:CURD 生成的列表页不含任何受控/形态配置,自动获得受控数据流 + 双端「表格/卡片」+ 视图按钮,模板零改动;PC 端缺省表格、手机端缺省卡片
**两条硬约束**:
1. 模板渲染容器恒带 `layui-form` 类与 lay-filter(框架自建容器已保证,页面无需关心)。表单控件的渲染美化与事件都要求 `.layui-form` 祖先,模板里写 switch/checkbox 依赖这条保证,缺失则全部失效。
-2. laytpl/Vue 的模板节点(`
-```
-
-```js
-layoutList: ['table', {name: 'mycard', title: '自定义卡', tplLayId: '#myCardTpl'}],
-```
-
-laytpl 语法实测(layui 2.10.1 内置 laytpl v2.0.0):`{{ d.x }}` 与 `{{= d.x }}` 都会 HTML 转义,`{{- d.x }}` 才是原样输出。`_cells`/`_operatHtml` 是渲染产物 HTML,必须用 `{{- }}`;纯文本用 `{{ }}` 转义更安全;`{{# }}` 写 JS 语句;列表上下文是 `d._list`。
-
-**Vue 形态(tplVueId)**,简版:
-
-```html
-
-
-```
-
-```js
-layoutList: ['table', {name: 'mycard', title: '自定义卡', tplVueId: '#myVueTpl'}],
-```
-
-- `v-for="d in rows"` 承接行循环;`_cells`/`_operatHtml` 必须 `v-html` 输出,纯文本字段用 `{{ }}` 原生插值(自动转义);列表上下文是 `list`
-- 行根元素带 `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.js` | render 配置块:layoutList 三形态 + layoutDefault 对象式按端默认(`{pc: 'table', mobile: 'card'}`) |
-| `extend/base/admin/view/system/admin/index.html` | usercardTpl 模板节点(`
+```
+
+```js
+layoutList: ['table', {name: 'mycard', title: '自定义卡', tplLayId: '#myCardTpl'}],
+```
## 四、实战配方:以商品卡为蓝本走一遍
@@ -101,10 +118,10 @@ layoutList: ['table', {name: 'usercard', title: '名片', tplVueId: '#usercardTp
### 第 1 步:index.html 写模板节点与页面级样式
-在根容器 `` 之后追加 `
@@ -130,7 +147,7 @@ layoutList: ['table', {name: 'usercard', title: '名片', tplVueId: '#usercardTp
同文件追加页面级 `