---
name: swing-cli-action-gen
description: "Swing System 按钮权限 Action 创建。从一个或多个 Vue 页面提取 v-permission，按 PAGE+权限标识去重，从按钮 tip/文本提取中文 displayName，关联 PAGE 路由并预览完整 batch-add payload，统一确认后批量创建。当用户说「创建按钮权限」「配置action」「按钮权限」「v-permission」「Action去重」「中文权限名称」时使用。"
metadata:
  requires:
    bins: ["node", "swing-cli"]
---

# swing-cli-action-gen — 按钮权限 Action 去重创建

> 从一个或多个 Vue 页面的 `v-permission` 提取权限标识，按 PAGE 去重并保留全部来源。平台 `name` 保持前端英文标识，`displayName` 使用中文业务名称。先展示完整变更集，统一确认后通过 `swing-cli action batch-add` 按页面创建。

## When to Use

- 为单个或多个 PAGE 路由配置按钮权限。
- 在 `swing-cli-menu-gen` 创建或同步页面后继续创建 Action。
- 对比 Vue `v-permission` 与平台现有 Action，只补齐缺失项。

## Safety Rules

- 先查后建，只创建缺失 Action，不更新或删除已有 Action。
- `name` 必须与 Vue `v-permission` 完全一致。
- Action 唯一键固定为 `(PAGE routeId/别名, name)`；同一 PAGE 内重复出现只创建一次，不同 PAGE 上的同名 Action 分别创建。
- `displayName` 必须是包含至少一个中文字符的业务名称，不得直接使用英文 `name`、权限前缀或文件名充当显示名称。
- `displayName`、`requestMethod`、`apiUrl` 或 `remark` 无法从代码和既有契约确定时，标记为 `⚠️ 待确认`，不得猜测或执行。
- 同一唯一键的重复来源出现不同中文名称、请求方法、API 地址或备注时，标记为 `🛑 冲突`，用户明确选择统一值前不得生成 payload。
- Step 3 的最终确认覆盖所有页面的完整 Action 变更集；参数未变化时，Step 4 不再逐页面或逐 Action 重复确认。
- 批量创建发生部分失败时立即停止后续页面，先查询实际结果并生成差异清单。

## Workflow

### [Step 1/5] 环境与站点确认

独立调用本技能时，依次执行：

```text
swing-cli config get
swing-cli auth status
swing-cli site list
```

用一张表展示并让用户一次确认 `server`、登录用户、认证状态、站点和 `siteId`。

如果本技能紧接同一任务中的 `swing-cli-menu-gen`：

- 复用已经确认的 `server` 和 `siteId`。
- 重新执行 `auth status` 验证登录仍有效。
- 环境未变化时只报告复用结果，不重复要求用户确认；发生变化时按独立调用流程重新确认。

### [Step 2/5] 收集、中文命名、去重并查询现有 Action

1. 接受一个或多个 `index.vue` 路径，使用结构化文件读取或项目可用的文本搜索工具提取每一次 `v-permission` 出现。每个原始项记录 PAGE、权限 name、来源文件绝对路径、行号、组件标签、中文候选文本和请求契约。
2. 解析每个页面对应的 PAGE 路由。已有页面使用真实 `routeId`；同一任务中由 `swing-cli-menu-gen` 计划创建的页面沿用 `R?` 别名和 `<R?.routeId>` 引用。
3. 按以下优先级确定中文 `displayName`，取到明确静态中文后停止：

| 优先级 | 中文名称来源 | 示例 |
|---|---|---|
| 1 | 图标按钮的静态 `tip` / `tooltip` | `<s-icon-button tip="冻结" ...>` → `冻结` |
| 2 | 普通按钮的可见中文文本 | `<a-button>批量冻结</a-button>` → `批量冻结` |
| 3 | 静态 `title` / `label` / `aria-label` | `title="解冻"` → `解冻` |
| 4 | 标准 CRUD 权限前缀映射 | `view-` → `查看` |
| 5 | 用户确认 | 动态绑定、无中文文本或自定义语义时必须确认 |

动态表达式（如 `:tip="actionLabel"`）不得执行或猜值；标记为 `⚠️ 待确认`。候选值不包含中文字符时也必须让用户提供中文名称。

4. 以 `(PAGE routeId/别名, name)` 分组去重，并保留每组的全部来源明细：

| 重复情况 | 处理 |
|---|---|
| 同一 PAGE、同一 name、参数一致 | 合并为一个 Action，累计来源次数和位置 |
| 同一 PAGE、同一 name、中文名称不同 | `🛑 冲突`，用户统一名称后才能继续 |
| 同一 PAGE、同一 name、请求契约不同 | `🛑 冲突`，核对 Controller/Service 后才能继续 |
| 不同 PAGE、同一 name | 不合并；分别挂载到各 PAGE |

5. 对每个已有 PAGE 执行：

```text
swing-cli action list --site <siteId> --route <routeId>
```

6. 使用相同唯一键与现有 Action 对比，只把缺失项标记为 `➕`。平台已存在同名 Action 时标记为 `✅`；若其 displayName 不是中文或参数与代码契约不一致，单独标记 `⚠️ 已有配置不一致`，本技能不自动更新。
7. 输出提取统计：原始出现次数、去重后唯一 Action 数、合并重复次数、待确认数、冲突数。冲突数不为 0 时不得进入 Step 3。

常用映射只能在代码语义一致时采用：

| 权限前缀 | displayName | requestMethod |
|---|---|---|
| add- | 新增 | POST |
| view- | 查看 | GET |
| edit- | 编辑 | PUT |
| remove- | 删除 | DELETE |

项目存在不同语义或 HTTP 方法时，以实际 Controller/Service 契约为准。

### [Step 3/5] 展示完整 Action 变更集并统一确认

> **核心原则：先查后对比，只创建缺失项。**

为待创建 Action 分配稳定别名 `A1`、`A2`……，并一次性展示以下内容。

#### 3a. 路由与 Action 整体树

沿用 `swing-cli-menu-gen` 的路由别名，使用户能直接看到 Action 挂载位置：

```text
➕ [R1] 库存台账 (MENU)
└── ➕ [R2] 原辅料库存台账 (PAGE, routeId: <R2.routeId>)
    ├── ✅ view-raw-inv (Action, 已有)
    ├── ➕ [A1] freeze-raw-inv (Action, 中文: 冻结, 来源: 2处)
    └── ➕ [A2] unfreeze-raw-inv (Action)
```

#### 3b. 完整参数表

| siteId | 页面别名 | PAGE 名称 | routeId/引用 | 状态 | Action 别名 | name | displayName | 中文名称来源 | requestMethod | apiUrl | remark | 来源次数 | 来源位置 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| <siteId> | R2 | 原辅料库存台账 | <R2.routeId> | ➕ | A1 | freeze-raw-inv | 冻结 | tip | PUT | /raw-sub-inv/freeze | - | 2 | `<index.vue>:88`, `<index.vue>:106` |

表格必须包含所有 `✅ 已有`、`➕ 新增`、`⚠️ 待确认`、`⚠️ 已有配置不一致` 和 `🛑 冲突` 项，并展开每个去重组的全部来源位置。只有无冲突、参数完整且状态为 `➕` 的唯一 Action 进入创建 payload。

#### 3c. 每个页面的完整 batch-add payload

按 PAGE 分组展示去重后的实际 JSON，字段不得省略。同一 payload 内 `name` 必须唯一：

```json
[
  {
    "name": "freeze-raw-inv",
    "displayName": "冻结",
    "requestMethod": "PUT",
    "apiUrl": "/raw-sub-inv/freeze"
  }
]
```

没有业务值的可选字段应在参数表中显示 `-`，JSON payload 中按 CLI 契约省略，不得传递伪造的 `"-"`。

#### 3d. 精确命令与变更摘要

```text
[R2 Actions] swing-cli action batch-add --site <siteId> --route <R2.routeId> --file <actions-file>
```

展示：涉及 PAGE 数量、原始出现次数、去重后唯一 Action 数量、合并重复数量、已有 Action 数量、新增 Action 数量、跳过数量、待确认数量、冲突数量、执行顺序和全部路由动态依赖。

用户对整体树、参数表、所有 payload 和全部命令进行一次确认。该确认只批准已展示的 Action，不批准任何未展示的 Action 或参数变化。

如果全部 Action 均为 `✅ 已有`，展示零变更摘要并跳过 Step 4。

### [Step 4/5] 按页面批量执行

1. 仅将 Step 3 中无冲突且参数完整的 `➕` 唯一 Action 写入各页面独立的 UTF-8 JSON 文件；写入前再次断言同一 PAGE payload 中 `name` 不重复且每个 `displayName` 包含中文字符。
2. 按已确认顺序执行 `swing-cli action batch-add`，参数未变化时不再次逐页或逐 Action 要求确认。
3. 若 Route ID 来自 `swing-cli-menu-gen`，先用真实 Route ID 替换 `<R?.routeId>`。
4. 持续输出每个页面和 Action 的 `执行中 / 成功 / 失败 / 未执行` 状态及返回 ID。
5. 任一页面批次出现失败时停止后续页面，立即查询该 PAGE 的 Action 列表并生成差异。

执行前或执行中发现页面、Route ID、payload、命令或 Action 数量与 Step 3 不同，必须停止并返回 Step 3 重新展示和确认。

### [Step 5/5] 验证

对每个涉及的 PAGE 执行：

```text
swing-cli action list --site <siteId> --route <routeId>
```

逐项核对 `name`、中文 `displayName`、`requestMethod`、`apiUrl` 和所属 Route ID，并断言同一 PAGE 下不存在重复 name。最后用与 Step 3 相同的树和参数表展示实际结果，标记 `✅ 已验证` 或 `❌ 不一致`，由用户确认交付结果。
