From f688eef43e7247e0e468488c0b1b4c94cc7655f4 Mon Sep 17 00:00:00 2001 From: augushong Date: Sun, 4 Oct 2026 01:44:19 +0000 Subject: [PATCH] =?UTF-8?q?docs(skills):=20=E6=8A=80=E8=83=BD=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E6=9B=B4=E6=96=B0=E4=B8=BA=E5=85=A8=E5=B1=80=E6=9D=83?= =?UTF-8?q?=E9=99=90=E5=88=A4=E5=AE=9A=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/ulthon-admin-table/SKILL.md | 57 ++++++++++---------- .agents/skills/ulthon-card-template/SKILL.md | 2 +- .agents/skills/ulthon-page-qa/SKILL.md | 40 +++++++------- 3 files changed, 48 insertions(+), 51 deletions(-) diff --git a/.agents/skills/ulthon-admin-table/SKILL.md b/.agents/skills/ulthon-admin-table/SKILL.md index ae15b77..db2ad15 100644 --- a/.agents/skills/ulthon-admin-table/SKILL.md +++ b/.agents/skills/ulthon-admin-table/SKILL.md @@ -41,28 +41,26 @@ CURD 生成器生成列表页骨架后,所有业务定制(按钮、字段渲 ``` app/admin/view// -├── index.html # 极简 HTML,仅 table 元素 + data-auth-* 权限标记 +├── index.html # 纯结构零权限声明,权限由 layout 全局 UA_AUTH 注入 ├── _common.js # init 对象(URL 配置等) └── index.js # ua.table.render({init, cols, toolbar?}) ``` -**index.html**(典型示例,仅 11 行): +**index.html**(典型示例): ```html
``` -**`data-auth-X` 是按钮权限显隐的关键**:JS 里的 `auth: 'X'` 必须能在 HTML 找到对应的 `data-auth-X`,且值为 1 才显示。详见第六章。 +**权限判定协议**:index.html 不写任何权限声明。页面加载时 layout 注入全局载荷 `window.UA_AUTH = {all, controller, nodes}`(服务端一次性构建,超管 `all:true`),前端 `ua.auth(node)` 据此全局判定按钮显隐:短名(不含 `/`)自动补当前控制器前缀,跨控制器直接写全节点(如 `'app.code_bind/scan'`,与注册节点逐字一致,动作段大小写敏感)。详见 5.4 与 5.7。 + +> **升级注意**:本机制的判定实现位于 admin-core.js,其静态引用带框架版本号缓存键(下游部署为 site_version)。升级框架后若按钮显隐异常,先强制刷新浏览器缓存(Ctrl+F5)再排查,避免旧缓存 JS 与新页面叠加造成误判。 ### 1.3 前后端链路 @@ -241,7 +239,7 @@ ua.table.render({ text: '导入', url: 'demo.index/import', method: 'open', // 见 5.3 method 表 - auth: 'import', // 对应 data-auth-import + auth: 'import', // 短名=当前控制器动作(等价 'demo.index/import') class: 'layui-btn layui-btn-warm layui-btn-sm', icon: 'fa fa-upload', title: '导入数据', @@ -308,7 +306,7 @@ ua.table.render({ | `url` | string \| function | `''` | 请求 URL;为 function 时接收 `(data, operat)` 动态构造 | | `method` | string | `'open'` | 见 5.3 method 表 | | `field` | string \| function | `'id'` | URL 拼接的主键字段名;为 function 时可动态构造 URL | -| `auth` | string | `'add'` | 权限节点名(对应 HTML 的 `data-auth-X`) | +| `auth` | string | `'add'` | 权限节点:短名=当前控制器动作,跨控制器写全节点(与注册节点逐字一致,详见 5.7) | | `class` | string | `''` | HTML class(layui-btn 样式) | | `icon` | string | `''` | 图标 class | | `extend` | string | `''` | 额外 HTML 属性(如 `data-full="true"`) | @@ -338,7 +336,7 @@ operat: [ text: '触发', url: init.triggerUrl, method: 'request', - auth: 'trigger', + auth: 'trigger', // 短名=当前控制器动作(等价 'system.timer_config/trigger') class: 'layui-btn layui-btn-warm layui-btn-xs', // 仅 run_type='manual' 时显示 _if: function(data) { @@ -356,15 +354,7 @@ operat: [ ### 5.7 跨控制器权限检查 -按钮跳转到**其他控制器**的页面时(如 box_sn 页面的"绑定"按钮跳转 `code_bind/scan`),`auth` 的权限节点不在当前控制器下。需要在 HTML 用自定义 key 声明跨控制器的 `data-auth` 属性。 - -**HTML**: - -```html - -``` - -**JS**: +按钮跳转到**其他控制器**的页面时(如 box_sn 页面的"绑定"按钮跳转 `code_bind/scan`),`auth` 直接写全节点即可,无需任何 HTML 中转声明: ```js [{ @@ -372,13 +362,21 @@ operat: [ method: 'tab', text: '绑定', title: '扫码绑定', - auth: 'bind', // 对应 HTML 中的 data-auth-bind + auth: 'app.code_bind/scan', // 全节点,与注册节点逐字一致 url: 'app.code_bind/scan', // 跨控制器 URL icon: 'fa fa-link' }] ``` -框架通过 `data-auth-{auth值}` 查找 HTML 属性做权限校验:有权限显示按钮,无权限自动隐藏。 +`ua.auth` 按全局允许集(layout 注入的 `window.UA_AUTH`)判定:有权限显示按钮,无权限自动隐藏。 + +**全节点写法约定**: + +- 与 `@NodeAnnotation` 注册的节点**逐字一致**:`ua.auth` 做字符串匹配,前端不做任何规范化转换 +- 动作段**大小写敏感**:注册为 `setMaster` 就写 `'system.host/setMaster'`,禁止 snake 化写成 `set-master`(parseNodeStr 只规范控制器段,动作段原样保留) +- 短名(不含 `/`)自动按"当前控制器/短名"解析,只适用于本控制器动作 + +**未注册动作的行为差异**:权限载荷不含 default_pass,前端对"不在允许集"的按钮一律保守隐藏。在 `default_auth_check=true` 的部署中,未注册动作后端仍按真实规则放行,即前端隐藏而后端放行,此时应前后端核对并补注册节点。旧协议(HTML `data-auth` 属性中转)仅作为下游旧页面的回退通道保留,新页面不使用。 ### 5.8 operat 数组结构 @@ -518,7 +516,7 @@ public function index() | `$selectWhere` | array 属性,`selectList()` 方法的过滤条件 | | `modify()` | 状态开关接口,`switch` 渲染器自动调用,接收 `id`/`field`/`value` | | `selectList()` | 下拉数据源接口,接收 `selectFields` 参数 | -| `@NodeAnnotation` | 控制器方法注解,生成权限节点(与 `data-auth-X` 对应) | +| `@NodeAnnotation` | 控制器方法注解,生成权限节点(前端 `ua.auth` 全局判定按钮显隐的依据) | **关联搜索示例**(`extend/base/admin/controller/mall/GoodsBase.php`): @@ -594,9 +592,9 @@ CURD 生成时会将关联模型的全部字段(如 `systemAdmin.*`)输出 **错误 6:权限节点不匹配(按钮不显示)** -- HTML 的 `data-auth-X` 必须存在 -- JS 里的 `auth: 'X'` 必须能找到对应的 `data-auth-X` -- `{:auth('controller/action')}` 的节点路径必须与控制器 `@NodeAnnotation` 一致 +- JS 里的 auth 值解析出的节点必须与控制器 `@NodeAnnotation` 注册的节点一致:短名等于"当前控制器/动作",跨控制器直接写全节点 +- 全节点与注册节点逐字比对,动作段大小写敏感(注册 `setMaster` 写成 `set-master` 即失配隐藏) +- 未注册动作前端保守隐藏;`default_auth_check=true` 部署下后端仍按真实规则放行,前后端核对后补注册节点 排查工具:`php think admin:permission:nodes`(见 [ulthon-permission-cli](./ulthon-permission-cli/SKILL.md)) @@ -604,10 +602,11 @@ CURD 生成时会将关联模型的全部字段(如 `systemAdmin.*`)输出 **"按钮不显示"排查步骤**: -1. 检查 HTML 是否声明了对应的 `data-auth-X`(用浏览器开发者工具看 table 元素) -2. 检查 `{:auth('xxx/yyy')}` 的返回值是否为 1(用超管账号测试排除权限问题) -3. 检查 JS 里 `auth: 'X'` 的 X 与 HTML `data-auth-X` 是否完全一致(大小写) +1. 浏览器控制台核对 `window.UA_AUTH`:确认按钮 auth 值解析出的节点在允许集内(短名自动补当前控制器前缀,全节点逐字匹配) +2. 检查 JS 里 auth 值与 `@NodeAnnotation` 注册节点是否逐字一致(动作段大小写敏感,禁止 snake 化动作段) +3. 用超管账号测试排除账号权限配置问题(超管 `all:true` 恒显) 4. 查看权限节点列表:`php think admin:permission:nodes` +5. 下游旧页面(data-auth 回退通道):检查 HTML 是否声明了对应的 `data-auth-X` 属性且值为 1。仅旧页面有此声明,新协议页面 index.html 无权限声明 **"搜索不生效"排查步骤**: @@ -787,6 +786,6 @@ layoutDefault: true, // 旧协议"自动判断",视 - [ulthon-scheme-curd-workflow](./ulthon-scheme-curd-workflow/SKILL.md):CURD 代码生成流程(生成本技能所定制的列表页骨架) - [ulthon-scheme-definition](./ulthon-scheme-definition/SKILL.md):Scheme 定义(决定生成哪些字段、字段类型) - [ulthon-page-api-dual-mode](./ulthon-page-api-dual-mode/SKILL.md):页面/接口同体机制(列表接口的 JSON 返回模式) -- [ulthon-permission-cli](./ulthon-permission-cli/SKILL.md):权限节点核对(`data-auth-X` 与 `@NodeAnnotation` 对不上时) +- [ulthon-permission-cli](./ulthon-permission-cli/SKILL.md):权限节点核对(按钮 auth 值与 `@NodeAnnotation` 注册节点对不上时) - [ulthon-tools-http-call](./ulthon-tools-http-call/SKILL.md):CLI 模拟请求验证列表接口 - [ulthon-page-qa](./ulthon-page-qa/SKILL.md):页面验证(AI 自测)与浏览器断言要点 diff --git a/.agents/skills/ulthon-card-template/SKILL.md b/.agents/skills/ulthon-card-template/SKILL.md index 50d6264..8be82ce 100644 --- a/.agents/skills/ulthon-card-template/SKILL.md +++ b/.agents/skills/ulthon-card-template/SKILL.md @@ -51,7 +51,7 @@ Vue 形态的 `v-for="d in rows"` 里的 `d`: |------|------| | `...row` | 行原始字段全部可用(username、title、price、create_time 等) | | `_cells` | 对象,field 到该列 templet 渲染产物 HTML 的映射。只有配了渲染器的列才有内容;纯文本列恒为空串,**勿引用,直接用原始字段**;隐藏列(col.hide)不产出对应键 | -| `_operatHtml` | 操作列渲染产物(编辑/删除等按钮 HTML,已按 `data-auth-*` 裁剪权限) | +| `_operatHtml` | 操作列渲染产物(编辑/删除等按钮 HTML,已按当前登录者权限裁剪) | | `_checked` | 该行是否选中(store 选中集合) | | `_disabled` | 该行是否禁选 | diff --git a/.agents/skills/ulthon-page-qa/SKILL.md b/.agents/skills/ulthon-page-qa/SKILL.md index 3abacef..e526be7 100644 --- a/.agents/skills/ulthon-page-qa/SKILL.md +++ b/.agents/skills/ulthon-page-qa/SKILL.md @@ -82,27 +82,25 @@ cols: [[ 完整 searchOp 操作符表见 [ulthon-admin-table](./ulthon-admin-table/SKILL.md) 第七章。 -### 1.4 按钮显隐:`data-auth-*`(按权限节点) +### 1.4 按钮显隐:UA_AUTH 全局权限判定 -权限按钮由两处配合控制: +按钮显隐由全局权限载荷决定,index.html 纯结构零权限声明: -1. **HTML 写入权限标记**:表格容器上以 `data-auth-{动作}="{:auth('控制器/动作')}"` 形式声明每个动作的权限值(1 有权限 / 0 无) - - 实测:`extend/base/admin/view/system/admin/index.html:3`: - ```html -
- ``` - - `{:auth(...)}` 是模板函数,返回当前登录账号对该权限节点的布尔判定 -2. **JS 按 auth 匹配显隐**:cols/toolbar 里的按钮对象带 `auth: '{动作}'`,框架读取对应 `data-auth-{动作}` 值决定渲染与否 - - 实测:`system/admin/index.js`(toolbar 默认含 add;操作列含 `password` 自定义按钮 + edit + delete) +1. **layout 注入全局载荷**:页面加载时服务端一次性注入 `window.UA_AUTH = {all, controller, nodes}`(`all`=超管标记;`controller`=当前控制器节点名;`nodes`=允许集 `{'控制器': ['动作', ...]}`,超管 `all:true` 时 nodes 为空数组) + - 实测:`extend/base/admin/view/layout/default.html`(内联 script,与 window.CONFIG 同级) + - 同体 JSON 模式(get_page_data)不携带该载荷,仅在页面 HTML 中存在 +2. **ua.auth(node) 全局判定**:按钮对象带 `auth` 值,框架经 `ua.checkAuth` 依序判定:`'1'` 恒显 → `CONFIG.IS_SUPER_ADMIN` → `ua.auth(node)` → `data-auth-{node}` 属性回退(仅旧页面) + - 短名(不含 `/`)自动补 `UA_AUTH.controller` 前缀;全节点逐字匹配,动作段大小写敏感(如 `system.host/setMaster` 禁止写成 `set-master`) - 默认值:`operat.auth = operat.auth || 'add'`(`admin-table.js:1105`)——未写 `auth` 的按钮按 `add` 节点判定 -> 验证口诀:**`auth: 'X'`**(JS)必须能在表格容器找到对应的 **`data-auth-X`**(HTML),且该值=1,按钮才会出现。节点路径必须和控制器 `@NodeAnnotation` 注解一致。 +**QA 校验口径**: + +- 页面源码断言 `window.UA_AUTH` 存在且形状正确(`{all, controller, nodes}` 三键齐全) +- 按钮显隐对应用户允许集:允许集内的按钮出现,不在允许集的消失,超管全显 +- 未注册动作按钮前端保守隐藏(载荷无 default_pass);`default_auth_check=true` 部署下后端仍按真实规则放行,属已知差异,以前后端核对为准 +- `data-auth-*` HTML 属性是下游旧页面的回退通道,新协议页面无此声明 + +> 验证口诀:按钮节点在 `UA_AUTH.nodes` 允许集内则显示,不在则隐藏(超管恒显)。节点路径必须和控制器 `@NodeAnnotation` 注解逐字一致(动作段大小写敏感)。 ### 1.5 按钮显隐:`_if`(按数据状态) @@ -179,7 +177,7 @@ php think tools:http:call --url="/admin/system.admin/index?get_page_data=1" --me - 用智能体自带浏览器能力(Playwright MCP / agent-browser)打开页面(需登录态),逐项核对: 1. 表格表头/列按 cols 渲染(字段、标题、关联字段显示正确) 2. 搜索区下拉选项 = Page Data 模式里 `select_list_*` 的内容(对照步骤 3 结果) - 3. 工具栏按钮:`data-auth-X=1` 的按钮出现、`=0` 的消失;`auth: 'X'`(JS)与 `data-auth-X`(HTML)一一对应 + 3. 工具栏按钮:显隐与用户允许集一致(`window.UA_AUTH`),有权限的出现、无权限的消失;旧页面回退通道才存在 `data-auth` 属性 4. 操作列按钮:`_if` 条件成立才出现 ### 步骤 5 — 搜索提交验证(网络抓包) @@ -287,8 +285,8 @@ visual-qa 技能(视觉对比 / 回归断言) | `#data-brage` 隐藏元素 | `extend/base/admin/view/layout/default.html` | | `data_brage` assign | `extend/base/common/controller/AdminControllerBase.php:273` | | 后端 assign `select_list_*` | `extend/base/admin/controller/system/HostBase.php` | -| `data-auth-{动作}` HTML 标记 | `extend/base/admin/view/system/admin/index.html:3` | -| `data-auth-set-master` 跨控制器权限示例 | `extend/base/admin/view/system/host/index.html:5` | +| `window.UA_AUTH` 载荷注入(layout 内联) | `extend/base/admin/view/layout/default.html` | +| set-master 按钮全节点写法(勘正:旧 `data-auth-set-master` 属性是"自定义键/驼峰动作段"样例而非跨控制器样例,auth() 参数与本页同控制器;属性已随新协议清理,JS 现写全节点) | `extend/base/admin/view/system/host/index.js:33` | | toolbar/auth(默认 add) | `extend/base/admin/view/system/admin/index.js` | | 操作列 auth(password 自定义按钮) | `extend/base/admin/view/system/admin/index.js:24-31` | | `operat.auth` 默认 'add' | `admin-table.js:1105` | @@ -305,7 +303,7 @@ visual-qa 技能(视觉对比 / 回归断言) - [ulthon-page-api-dual-mode](./ulthon-page-api-dual-mode/SKILL.md):页面/接口同体的触发条件与机制(本技能 1.6 节的依据) - [ulthon-tools-http-call](./ulthon-tools-http-call/SKILL.md):CLI HTTP 调用工具用法(本技能三模式的执行工具) - [ulthon-scheme-curd-workflow](./ulthon-scheme-curd-workflow/SKILL.md):标准开发流程,本技能是其「功能验证」环节的页面级细则 -- [ulthon-permission-cli](./ulthon-permission-cli/SKILL.md):`data-auth-*` / `auth` 注解节点对不上时,用它核对节点 +- [ulthon-permission-cli](./ulthon-permission-cli/SKILL.md):按钮 auth 值与注解注册节点对不上时,用它核对节点 - `playwright` / `visual-qa`(内置技能):前端浏览器验证与视觉对比 ### 5.3 计划假设修正记录