---
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<XxxBean>`
- `getXxxTotal(@Body)` — 分页合计，返回 `BaseResultBean<ManifestTotalDto>`
- 批量操作 API — 返回 `BaseResultBean<Boolean>`
- 下拉列表 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<List<KeyValue>>`
- 统计字段 `MutableLiveData<String>`
- 适配器配置：`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
<!-- 根容器必须使用 color_f2 背景色 -->
<LinearLayout
    android:background="@color/color_f2"
    android:orientation="vertical">
```

**2. 搜索区规范**：
```xml
<!-- 搜索区：marginHorizontal=10dp, marginTop=10dp, gravity=center_vertical -->
<LinearLayout
    android:layout_marginHorizontal="10dp"
    android:layout_marginTop="10dp"
    android:gravity="center_vertical">

    <!-- 搜索控件使用 PadSearchLayout -->
    <PadSearchLayout
        type="@{SearchLayoutType.DATE}"
        hint='@{"请选择航班日期"}'
        icon="@{@drawable/img_date}"
        value="@={viewModel.date}"
        android:layout_width="0dp"
        android:layout_weight="1" />

    <!-- 搜索按钮：img_search 图标，36x36dp，padding=2dp -->
    <LinearLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:gravity="center_vertical|start"
        android:paddingHorizontal="24dp">
        <ImageView
            android:layout_width="36dp"
            android:layout_height="36dp"
            android:padding="2dp"
            android:onClick="@{()-> viewModel.searchClick()}"
            android:src="@drawable/img_search" />
    </LinearLayout>
</LinearLayout>
```

**3. 底部统计栏规范**（两种变体，根据截图选择）：

**变体 A — 深蓝色底部栏**（多数列表页使用）：
```xml
<LinearLayout
    android:layout_height="50dp"
    android:background="@color/color_bottom_layout"
    android:gravity="center_vertical"
    android:paddingHorizontal="15dp">
    <TextView
        android:text='@{"合计："+viewModel.count+"条"}'
        android:textColor="@color/white"
        android:textSize="18sp"
        android:textStyle="bold" />
</LinearLayout>
```

**变体 B — 白色底部栏**：
```xml
<LinearLayout
    android:layout_height="50dp"
    android:background="@color/white"
    android:gravity="center_vertical"
    android:paddingHorizontal="15dp">
    <TextView
        android:text='@{"合计:" + viewModel.count + "条"}'
        android:textColor="@color/bottom_tool_tips_text_color"
        android:textSize="18sp"
        android:textStyle="bold" />
</LinearLayout>
```

**4. 详情页/表单页规范**：
- 必须使用 **PadDataLayoutNew**（非旧版 PadDataLayout）
- 卡片背景：`@drawable/bg_white_radius_8`，padding `15dp`
- 行间距：`marginTop="8dp"`
- 三列标准布局，每列 `layout_weight="1"`
```xml
<ScrollView android:fillViewport="true">
    <LinearLayout android:padding="15dp" android:orientation="vertical">
        <LinearLayout android:background="@drawable/bg_white_radius_8"
            android:padding="15dp" android:orientation="vertical">
            <LinearLayout android:orientation="horizontal">
                <PadDataLayoutNew layout_weight="1" enable="@{false}"
                    title='@{"航班日期"}' type="@{DataLayoutType.INPUT}"
                    value='@{viewModel.dataBean.fdate}' />
                <!-- 更多列... -->
            </LinearLayout>
            <LinearLayout android:orientation="horizontal" android:layout_marginTop="8dp">
                <!-- 第二行... -->
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
</ScrollView>
```

---

#### Activity 布局完整结构

```xml
<layout>
  <data>
    <import SearchLayoutType />
    <variable viewModel />
    <variable activity />  <!-- 类型 3 或有 Dialog 操作时 -->
  </data>
  <LinearLayout background="@color/color_f2" vertical>      <!-- ← 新规范：背景色 -->
    <include title_tool_bar />
    <!-- 搜索区：marginHorizontal=10dp, marginTop=10dp -->
    <LinearLayout horizontal marginHorizontal="10dp" marginTop="10dp">
      PadSearchLayout × N + 搜索按钮(img_search, 36dp)       <!-- ← 新规范：搜索按钮 -->
    </LinearLayout>
    <!-- 列表 -->
    <SmartRefreshLayout> <RecyclerView /> </SmartRefreshLayout>
    <!-- 底部栏：50dp，根据截图选择深蓝/白色变体 -->          <!-- ← 新规范：底部栏 -->
    <LinearLayout height="50dp" background="..." paddingHorizontal="15dp">
      <TextView textSize="18sp" textStyle="bold" />
    </LinearLayout>
  </LinearLayout>
</layout>
```

#### 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<XxxBinding, XxxViewModel>() {
    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<String>(ConstantEvent.EVENT_REFRESH).observe(this) { viewModel.refresh() }

        // 初始化下拉列表（如有）
        viewModel.initAgentList()
        viewModel.initSpecialCodeList()

        viewModel.refresh()
    }
}
```

### 第 11 步：注册 Activity + 菜单入口

1. **AndroidManifest.xml**：在其他 gjj Activity 注册附近添加：
```xml
<activity android:name="com.lukouguoji.xxx.activity.XxxActivity"
    android:configChanges="orientation|keyboardHidden"
    android:exported="false" android:screenOrientation="userLandscape" />
```

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 导入等）
