diff --git a/.claude/skills/new-page/SKILL.md b/.claude/skills/new-page/SKILL.md new file mode 100644 index 0000000..ffbfd3c --- /dev/null +++ b/.claude/skills/new-page/SKILL.md @@ -0,0 +1,465 @@ +--- +name: new-page +description: 根据设计截图和菜单入口名称,在 AirLogistics 项目中创建新的业务页面。自动分析截图判断页面类型(6种典型类型之一),规划所需文件,然后完整实现(Kotlin + XML + 路由注册 + 菜单接入)。当用户说"创建页面"、"新建页面"、"实现这个页面"、"做这个页面"或提供设计截图要求开发时触发。 +--- + +# 新建业务页面 + +根据设计截图和菜单入口名称,在 AirLogistics 项目中完整创建一个新的业务页面。 + +## 前置要求 + +用户需要提供: +1. **设计截图**(必须)— 页面 UI 设计图 +2. **菜单入口名称**(必须)— 页面在首页菜单中显示的名称 +3. **所属模块**(可选)— 如 module_gjj(国际进港)、module_gjc(国际出港)等,可从截图标题推断 + +## 执行步骤 + +### 第 1 步:分析截图,判定页面类型 + +仔细阅读截图,提取以下信息: + +1. **页面标题**(标题栏文字) +2. **搜索区字段**(每个搜索控件的 hint 文本、类型:日期/输入/下拉/扫码) +3. **列表项字段**(每个数据字段的标签名和显示格式) +4. **底部操作栏**(统计项 + 操作按钮) +5. **特殊交互**(全选、展开/收起、右箭头、子列表等) + +然后对照 CLAUDE.md 中的 **6 种典型页面类型**判定: + +| 类型 | 关键特征 | 判定依据 | +|------|----------|----------| +| **类型 1:列表查询页** | 搜索 + 分页列表 + 底部统计 | 无全选、无勾选,纯查看 | +| **类型 2:多选列表 + 批量操作页** | 类型 1 + 全选按钮 + 飞机图标选中态 + 操作按钮 | 有全选、有批量操作按钮(如"清除提货") | +| **类型 3:嵌套多选列表页** | 类型 2 + 子列表(展开/收起)+ 主子联动全选 | 有展开按钮、列表项内含子 RecyclerView | +| **类型 4:Tab 详情页** | 自定义 Tab 栏 + ViewPager2 + 多 Fragment | 有 Tab 切换、无列表搜索 | +| **类型 5:编辑表单页** | ScrollView + PadDataLayoutNew 表单 + 保存/取消 | 有可编辑字段、有保存按钮 | +| **类型 6:添加表单页** | 类型 5 + 输入回调 + 实时计算 | 有 setRefreshCallBack 联动 | + +**向用户确认**判定结果,格式: + +``` +📋 页面分析结果: + +页面标题:XXX +页面类型:类型 N — XXXX +所属模块:module_xxx + +搜索条件: + 1. XXX(DATE) + 2. XXX(SPINNER) + 3. XXX(INPUT + 扫码) + +列表字段(第一行):XXX | XXX | XXX +列表字段(第二行):XXX | XXX | XXX + +底部操作:全选 + 统计(合计/总件数/总重量) + [操作按钮名称] + +是否正确?确认后开始实现。 +``` + +### 第 2 步:查找参考模板并提取 UI 设计规范 + +根据判定的页面类型,在项目中找到**同类型的最新参考实现**,并严格提取 UI 设计规范。 + +**查找策略**(优先级从高到低): +1. 优先查找**最近提交的同类型页面**(最新的页面代表最新的 UI 规范) +2. 其次在**同模块**内查找同类型页面 +3. 最后在 **module_gjc**(国际出港)查找 + +**最新典型参考页面**(2024年后新增,代表当前 UI 规范): + +| 页面类型 | 参考页面 | 布局文件 | +|----------|----------|----------| +| 列表查询页 | 航班查询列表 | `activity_flight_query_list.xml` / `item_flight_query_list.xml` | +| 列表查询页 | 日志查询页 | `activity_log_query.xml` / `item_log_query.xml` | +| 详情页 | 航班查询详情 | `activity_flight_query_details.xml` | +| 详情页 | 日志详情 | `activity_log_detail.xml` | + +**必须读取的参考文件**: +- 上述最新典型参考页面中**与当前页面类型匹配的布局 XML**(Activity 布局 + Item 布局) +- 对应的 Activity、ViewModel、ViewHolder Kotlin 代码 +- Dialog(如有批量操作弹窗) + +**UI 设计规范提取(必做)**: + +读取参考布局后,必须逐一确认以下规范项,并在开发计划中明确列出: + +| 规范项 | 必须确认的内容 | +|--------|---------------| +| 页面背景色 | 根容器 `background` 属性(新规范:`@color/color_f2`) | +| 搜索区样式 | 使用的搜索控件类型、间距、布局方式 | +| 搜索按钮样式 | 图标资源、尺寸、style(新规范:`@drawable/img_search`,36dp) | +| 底部栏背景色 | 背景色 + 文字颜色(新规范有两种变体) | +| 底部栏文字样式 | 字号、粗细、颜色 | +| 列表项背景 | 背景、间距、padding | +| 详情页卡片样式 | 背景、圆角、padding、行间距 | +| 表单控件 | 使用 PadDataLayoutNew(非旧版 PadDataLayout) | + +**同时检查**: +- 该页面需要的**下拉列表数据源 API** 是否已存在(代理人、特码等) +- 该页面需要的 **Bean 类**是否已存在,或需要新建 +- `ARouterConstants` 中是否已有对应路由常量 +- `Constant.AuthName` 中是否已有对应权限名 + +### 第 3 步:制定文件清单 + +列出所有需要**新建**和**修改**的文件。 + +**新建文件清单**(根据页面类型调整): + +| 类别 | 文件 | 路径 | +|------|------|------| +| Bean | `XxxBean.kt`(如需) | `module_base/.../bean/` | +| Activity | `XxxActivity.kt` | `module_xxx/.../activity/` | +| ViewModel | `XxxViewModel.kt` | `module_xxx/.../viewModel/` | +| ViewHolder | `XxxViewHolder.kt` | `module_xxx/.../holder/` | +| Dialog | `XxxDialogModel.kt`(如需) | `module_xxx/.../dialog/` | +| Activity 布局 | `activity_xxx.xml` | `module_xxx/.../res/layout/` | +| Item 布局 | `item_xxx.xml` | `module_xxx/.../res/layout/` | +| Dialog 布局 | `dialog_xxx.xml`(如需) | `module_xxx/.../res/layout/` | + +**修改文件清单**(固定): + +| 文件 | 修改内容 | +|------|----------| +| `Api.kt` | 添加 API 接口方法 + import | +| `ARouterConstants.kt` | 添加路由常量(如不存在) | +| `Constant.kt` | 添加 AuthName 常量(如不存在) | +| `AndroidManifest.xml` | 注册 Activity | +| `HomeFragment.kt` | 添加菜单项 + 点击路由处理 | +| 旧版 Activity(如有) | 注释掉 `@Route` 注解避免冲突 | + +### 第 4 步:创建 Bean(如需) + +如果截图中的列表字段与现有 Bean 不匹配,创建新的 Bean 类。 + +**规则**: +- 放在 `module_base/.../bean/` 目录下 +- 如果是类型 2/3(多选),必须包含 `ObservableBoolean` 选中状态: + +```kotlin +val checked: ObservableBoolean = ObservableBoolean(false) +var isSelected: Boolean + get() = checked.get() + set(value) = checked.set(value) +``` + +- 字段类型映射:数字用 `Int/Long/Double`,文本用 `String = ""`,时间用 `String = ""` +- 在 `Api.kt` 的 import 区按字母顺序添加 import + +### 第 5 步:添加 API 接口 + +在 `Api.kt` 中添加 API 方法。 + +**标准 API 组合**(根据页面需要选取): +- `getXxxList(@Body)` — 分页查询,返回 `PageInfo` +- `getXxxTotal(@Body)` — 分页合计,返回 `BaseResultBean` +- 批量操作 API — 返回 `BaseResultBean` +- 下拉列表 API(代理人、特码等)— 如不存在则添加 + +**API 路径命名**:先使用 `ModuleName/methodName` 格式占位(如 `IntImpPickUpRecord/pageQuery`),后续由第 13 步替换为真实接口路径 + +### 第 6 步:添加路由和权限常量 + +1. 在 `ARouterConstants.kt` 添加路由(如不存在): +```kotlin +const val ACTIVITY_URL_XXX = "/module/XxxActivity" +``` + +2. 在 `Constant.kt` 的 `AuthName` 中添加权限名(如不存在): +```kotlin +const val XxxPage = "AppXxxPage" +``` + +### 第 7 步:创建 ViewHolder + +根据 item 布局创建 ViewHolder。 + +**类型 1**:基础绑定 +**类型 2**:增加图标点击切换 `checked` 状态 +**类型 3**:增加子列表 `setCommonAdapter` + 展开按钮 + 父子联动 + +### 第 8 步:创建 ViewModel + +**必须包含的元素**(根据截图): +- 搜索条件 `MutableLiveData`(与搜索区对应) +- 下拉列表数据源 `MutableLiveData>` +- 统计字段 `MutableLiveData` +- 适配器配置:`itemViewHolder` + `itemLayoutId` +- `searchClick()` 方法 +- `getData()` override:调用列表 API + 统计 API + +**类型 2 额外**:`isAllChecked` + `checkAllClick()` + 批量操作方法 +**类型 3 额外**:`isAllExpanded` + `toggleAllExpand()` + 联动全选逻辑 + +**下拉列表初始化**: +- 代理人:`DictUtils.getAgentList()` / `NetApply.api.getIntImpAgentList()` / `getIntExpAgentList()` +- 特码:`DictUtils.getSpecialCodeList(flag, ieFlag, parentcode)` +- 其他字典:`DictUtils` 或自定义 API + +### 第 9 步:创建布局文件 + +**⚠️ 重要:所有布局必须严格遵循最新 UI 设计规范,参照第 2 步中提取的规范项。** + +--- + +#### 新版 UI 设计规范(强制执行) + +**1. 页面背景色**: +```xml + + +``` + +**2. 搜索区规范**: +```xml + + + + + + + + + + + +``` + +**3. 底部统计栏规范**(两种变体,根据截图选择): + +**变体 A — 深蓝色底部栏**(多数列表页使用): +```xml + + + +``` + +**变体 B — 白色底部栏**: +```xml + + + +``` + +**4. 详情页/表单页规范**: +- 必须使用 **PadDataLayoutNew**(非旧版 PadDataLayout) +- 卡片背景:`@drawable/bg_white_radius_8`,padding `15dp` +- 行间距:`marginTop="8dp"` +- 三列标准布局,每列 `layout_weight="1"` +```xml + + + + + + + + + + + + + +``` + +--- + +#### Activity 布局完整结构 + +```xml + + + + + + + + + + + PadSearchLayout × N + 搜索按钮(img_search, 36dp) + + + + + + + + + +``` + +#### Item 布局(从截图精确还原每一行每一列) + +- 逐行对照截图中的字段顺序和标签文本 +- 使用 `completeSpace` 对齐 Key 文本 +- 运单号等关键字段用 `@color/colorPrimary` +- 类型 2/3 左侧有飞机图标:`loadImage="@{bean.checked.get() ? @drawable/img_plane_s : @drawable/img_plane}"` +- 列表项背景统一使用 `@drawable/bg_item` +- 间距统一:`marginHorizontal="15dp"`, `marginVertical="5dp"`, `padding="10dp"` + +**关键原则:务必尽可能还原截图上的页面设计,不推测不假想。** + +### 第 10 步:创建 Activity + +**固定结构**: +```kotlin +@Route(path = ARouterConstants.ACTIVITY_URL_XXX) +class XxxActivity : BaseBindingActivity() { + override fun layoutId() = R.layout.activity_xxx + override fun viewModelClass() = XxxViewModel::class.java + + override fun initOnCreate(savedInstanceState: Bundle?) { + setBackArrow("页面标题") // 与截图标题一致 + binding.viewModel = viewModel + binding.activity = this // 类型 3 或有 Dialog 时 + + // 类型 2/3:观察全选状态 + viewModel.isAllChecked.observe(this) { binding.checkIcon.alpha = if (it) 1.0f else 0.5f } + + // 绑定分页 + viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this) + + // 监听刷新事件 + FlowBus.with(ConstantEvent.EVENT_REFRESH).observe(this) { viewModel.refresh() } + + // 初始化下拉列表(如有) + viewModel.initAgentList() + viewModel.initSpecialCodeList() + + viewModel.refresh() + } +} +``` + +### 第 11 步:注册 Activity + 菜单入口 + +1. **AndroidManifest.xml**:在其他 gjj Activity 注册附近添加: +```xml + +``` + +2. **HomeFragment.kt**: + - 在对应模块的菜单列表区添加 `RightMenu` 项(图标 + 标题) + - 在 onClick 处理区添加路由跳转 + +3. **旧版 Activity**(如有):注释掉 `@Route` 注解 + +### 第 12 步:编译验证 + +```bash +./gradlew assembleDebug +``` + +编译必须通过(0 errors)。如有错误,立即修复后重新编译。 + +### 第 13 步:查找并对接 API 接口 + +页面创建并编译通过后,通过"空港集团 - API 文档"Apifox MCP 工具查找真实接口,替换第 5 步中的占位路径。 + +**A. 基础接口查找(每次必查):** + +1. 根据页面所属业务路径(如"国际进港 → 原始舱单"),在 Apifox MCP 中按模块目录搜索 +2. 查找以下基础接口: + - **列表接口**(分页查询,如 `pageQuery`、`list`) + - **合计接口**(统计,如 `total`、`count`、`statistics`) + - **修改/保存接口**(如 `update`、`save`、`edit`) + - **删除接口**(如 `delete`、`remove`) + +**B. 智能接口匹配(根据页面分析):** + +3. 根据**页面类型特性**查找对应接口: + - 类型 2(多选批量操作)→ 查找批量操作接口(如批量删除、批量确认等) + - 类型 3(嵌套列表)→ 查找子列表相关接口 + - 类型 5/6(表单页)→ 查找详情查询接口、下拉选项字典接口 + - 类型 4(Tab 详情)→ 查找各 Tab 对应的数据接口 +4. 根据**页面中的按钮和文案**,逐一匹配对应接口: + - 例如页面有"审核"按钮 → 查找审核接口 + - 例如页面有"导出"按钮 → 查找导出接口 + - 例如页面有"打印"按钮 → 查找打印相关接口 + - 例如底部栏有"清除提货"按钮 → 查找清除提货接口 + - 例如搜索区有下拉框(代理人、状态等)→ 查找对应的字典/下拉数据接口 +5. 根据**截图中可见的交互元素**,推断可能需要的接口: + - 列表项有右箭头 → 可能需要详情接口 + - 列表项有编辑图标 → 可能需要编辑/更新接口 + - 有扫码图标 → 可能需要扫码查询接口 + +**C. 对接与校准:** + +6. 找到接口后,更新 `Api.kt` 中的占位路径为真实路径 +7. 根据接口的请求参数和返回字段结构,校准 Bean 类的字段名和类型 +8. 遵循 memory 中的 API 搜索原则:不跨模块混用接口,遇到不确定的接口询问用户确认 + +### 第 14 步:重新编译验证 + +对接真实 API 后重新编译,确保无错误: + +```bash +./gradlew assembleDebug +``` + +编译必须通过(0 errors)。如有错误,立即修复后重新编译。 + +## 注意事项 + +### UI 设计规范(强制) + +- **必须使用最新 UI 规范**:不管是新增页面、覆盖旧页面还是修改旧页面,都必须完全采用最新典型参考页面的 UI 设计规范 +- **页面背景色**:根容器必须使用 `@color/color_f2`(`#F2F2F2`),禁止使用旧的白色或其他背景 +- **搜索按钮**:使用 `@drawable/img_search` 图标(36x36dp + padding 2dp),不使用旧的 `iv_search_action` style +- **底部栏**:高度 50dp,文字 18sp bold,根据截图选择深蓝色(`@color/color_bottom_layout` + 白字)或白色(`@color/white` + `@color/bottom_tool_tips_text_color`) +- **详情页/表单页**:必须使用 **PadDataLayoutNew**(非旧版 PadDataLayout),卡片用 `bg_white_radius_8` +- **严格匹配典型页面**:在写开发计划前,必须先读取最新典型参考页面(如航班查询列表、日志查询页等),总结并列出 UI 设计规范要点 + +### 通用规则 + +- **资源引用必须存在**:使用 drawable/color/mipmap 前确认资源存在,不存在则换用已有资源 +- **import 路径查阅 CLAUDE.md**:基类和扩展函数的正确 import 路径参见开发指南的 Import 速查表 +- **不创建不需要的文件**:如果截图中没有 Dialog 弹窗,不要创建 Dialog 文件 +- **Bean 复用优先**:如果现有 Bean 的字段足以覆盖截图需求,直接复用 +- **API 接口对接**:页面创建完成后,通过空港集团 API 文档 MCP 查找真实接口并替换占位路径。遵循 API 搜索原则,按业务模块目录查找,不跨模块混用 +- **DataBinding 规则**:遵循 CLAUDE.md 中的 DataBinding 关键规则(双向绑定、字符串拼接、View 导入等)