19 KiB
name, description
| name | description |
|---|---|
| new-page | 根据设计截图和菜单入口名称,在 AirLogistics 项目中创建新的业务页面。自动分析截图判断页面类型(6种典型类型之一),规划所需文件,然后完整实现(Kotlin + XML + 路由注册 + 菜单接入)。当用户说"创建页面"、"新建页面"、"实现这个页面"、"做这个页面"或提供设计截图要求开发时触发。 |
新建业务页面
根据设计截图和菜单入口名称,在 AirLogistics 项目中完整创建一个新的业务页面。
前置要求
用户需要提供:
- 设计截图(必须)— 页面 UI 设计图
- 菜单入口名称(必须)— 页面在首页菜单中显示的名称
- 所属模块(可选)— 如 module_gjj(国际进港)、module_gjc(国际出港)等,可从截图标题推断
执行步骤
第 1 步:分析截图,判定页面类型
仔细阅读截图,提取以下信息:
- 页面标题(标题栏文字)
- 搜索区字段(每个搜索控件的 hint 文本、类型:日期/输入/下拉/扫码)
- 列表项字段(每个数据字段的标签名和显示格式)
- 底部操作栏(统计项 + 操作按钮)
- 特殊交互(全选、展开/收起、右箭头、子列表等)
然后对照 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 设计规范。
查找策略(优先级从高到低):
- 优先查找最近提交的同类型页面(最新的页面代表最新的 UI 规范)
- 其次在同模块内查找同类型页面
- 最后在 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选中状态:
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 步:添加路由和权限常量
- 在
ARouterConstants.kt添加路由(如不存在):
const val ACTIVITY_URL_XXX = "/module/XxxActivity"
- 在
Constant.kt的AuthName中添加权限名(如不存在):
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. 页面背景色:
<!-- 根容器必须使用 color_f2 背景色 -->
<LinearLayout
android:background="@color/color_f2"
android:orientation="vertical">
2. 搜索区规范:
<!-- 搜索区: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 — 深蓝色底部栏(多数列表页使用):
<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 — 白色底部栏:
<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,padding15dp - 行间距:
marginTop="8dp" - 三列标准布局,每列
layout_weight="1"
<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 布局完整结构
<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
固定结构:
@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 + 菜单入口
- AndroidManifest.xml:在其他 gjj Activity 注册附近添加:
<activity android:name="com.lukouguoji.xxx.activity.XxxActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false" android:screenOrientation="userLandscape" />
-
HomeFragment.kt:
- 在对应模块的菜单列表区添加
RightMenu项(图标 + 标题) - 在 onClick 处理区添加路由跳转
- 在对应模块的菜单列表区添加
-
旧版 Activity(如有):注释掉
@Route注解
第 12 步:编译验证
./gradlew assembleDebug
编译必须通过(0 errors)。如有错误,立即修复后重新编译。
第 13 步:查找并对接 API 接口
页面创建并编译通过后,通过"空港集团 - API 文档"Apifox MCP 工具查找真实接口,替换第 5 步中的占位路径。
A. 基础接口查找(每次必查):
- 根据页面所属业务路径(如"国际进港 → 原始舱单"),在 Apifox MCP 中按模块目录搜索
- 查找以下基础接口:
- 列表接口(分页查询,如
pageQuery、list) - 合计接口(统计,如
total、count、statistics) - 修改/保存接口(如
update、save、edit) - 删除接口(如
delete、remove)
- 列表接口(分页查询,如
B. 智能接口匹配(根据页面分析):
- 根据页面类型特性查找对应接口:
- 类型 2(多选批量操作)→ 查找批量操作接口(如批量删除、批量确认等)
- 类型 3(嵌套列表)→ 查找子列表相关接口
- 类型 5/6(表单页)→ 查找详情查询接口、下拉选项字典接口
- 类型 4(Tab 详情)→ 查找各 Tab 对应的数据接口
- 根据页面中的按钮和文案,逐一匹配对应接口:
- 例如页面有"审核"按钮 → 查找审核接口
- 例如页面有"导出"按钮 → 查找导出接口
- 例如页面有"打印"按钮 → 查找打印相关接口
- 例如底部栏有"清除提货"按钮 → 查找清除提货接口
- 例如搜索区有下拉框(代理人、状态等)→ 查找对应的字典/下拉数据接口
- 根据截图中可见的交互元素,推断可能需要的接口:
- 列表项有右箭头 → 可能需要详情接口
- 列表项有编辑图标 → 可能需要编辑/更新接口
- 有扫码图标 → 可能需要扫码查询接口
C. 对接与校准:
- 找到接口后,更新
Api.kt中的占位路径为真实路径 - 根据接口的请求参数和返回字段结构,校准 Bean 类的字段名和类型
- 遵循 memory 中的 API 搜索原则:不跨模块混用接口,遇到不确定的接口询问用户确认
第 14 步:重新编译验证
对接真实 API 后重新编译,确保无错误:
./gradlew assembleDebug
编译必须通过(0 errors)。如有错误,立即修复后重新编译。
注意事项
UI 设计规范(强制)
- 必须使用最新 UI 规范:不管是新增页面、覆盖旧页面还是修改旧页面,都必须完全采用最新典型参考页面的 UI 设计规范
- 页面背景色:根容器必须使用
@color/color_f2(#F2F2F2),禁止使用旧的白色或其他背景 - 搜索按钮:使用
@drawable/img_search图标(36x36dp + padding 2dp),不使用旧的iv_search_actionstyle - 底部栏:高度 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 导入等)