Files
aerologic-app/.claude/skills/new-page/SKILL.md
2026-04-12 20:23:26 +08:00

19 KiB
Raw Blame History

name, description
name description
new-page 根据设计截图和菜单入口名称,在 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
类型 4Tab 详情页 自定义 Tab 栏 + ViewPager2 + 多 Fragment 有 Tab 切换、无列表搜索
类型 5编辑表单页 ScrollView + PadDataLayoutNew 表单 + 保存/取消 有可编辑字段、有保存按钮
类型 6添加表单页 类型 5 + 输入回调 + 实时计算 有 setRefreshCallBack 联动

向用户确认判定结果,格式:

📋 页面分析结果:

页面标题XXX
页面类型:类型 N — XXXX
所属模块module_xxx

搜索条件:
  1. XXXDATE
  2. XXXSPINNER
  3. XXXINPUT + 扫码)

列表字段第一行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

必须读取的参考文件

  • 上述最新典型参考页面中与当前页面类型匹配的布局 XMLActivity 布局 + Item 布局)
  • 对应的 Activity、ViewModel、ViewHolder Kotlin 代码
  • Dialog如有批量操作弹窗

UI 设计规范提取(必做)

读取参考布局后,必须逐一确认以下规范项,并在开发计划中明确列出:

规范项 必须确认的内容
页面背景色 根容器 background 属性(新规范:@color/color_f2
搜索区样式 使用的搜索控件类型、间距、布局方式
搜索按钮样式 图标资源、尺寸、style新规范@drawable/img_search36dp
底部栏背景色 背景色 + 文字颜色(新规范有两种变体)
底部栏文字样式 字号、粗细、颜色
列表项背景 背景、间距、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 步:添加路由和权限常量

  1. ARouterConstants.kt 添加路由(如不存在):
const val ACTIVITY_URL_XXX = "/module/XxxActivity"
  1. Constant.ktAuthName 中添加权限名(如不存在):
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 图标36x36dppadding=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_8padding 15dp
  • 行间距: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 + 菜单入口

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

    • 在对应模块的菜单列表区添加 RightMenu 项(图标 + 标题)
    • 在 onClick 处理区添加路由跳转
  2. 旧版 Activity(如有):注释掉 @Route 注解

第 12 步:编译验证

./gradlew assembleDebug

编译必须通过0 errors。如有错误立即修复后重新编译。

第 13 步:查找并对接 API 接口

页面创建并编译通过后,通过"空港集团 - API 文档"Apifox MCP 工具查找真实接口,替换第 5 步中的占位路径。

A. 基础接口查找(每次必查):

  1. 根据页面所属业务路径(如"国际进港 → 原始舱单"),在 Apifox MCP 中按模块目录搜索
  2. 查找以下基础接口:
    • 列表接口(分页查询,如 pageQuerylist
    • 合计接口(统计,如 totalcountstatistics
    • 修改/保存接口(如 updatesaveedit
    • 删除接口(如 deleteremove

B. 智能接口匹配(根据页面分析):

  1. 根据页面类型特性查找对应接口:
    • 类型 2多选批量操作→ 查找批量操作接口(如批量删除、批量确认等)
    • 类型 3嵌套列表→ 查找子列表相关接口
    • 类型 5/6表单页→ 查找详情查询接口、下拉选项字典接口
    • 类型 4Tab 详情)→ 查找各 Tab 对应的数据接口
  2. 根据页面中的按钮和文案,逐一匹配对应接口:
    • 例如页面有"审核"按钮 → 查找审核接口
    • 例如页面有"导出"按钮 → 查找导出接口
    • 例如页面有"打印"按钮 → 查找打印相关接口
    • 例如底部栏有"清除提货"按钮 → 查找清除提货接口
    • 例如搜索区有下拉框(代理人、状态等)→ 查找对应的字典/下拉数据接口
  3. 根据截图中可见的交互元素,推断可能需要的接口:
    • 列表项有右箭头 → 可能需要详情接口
    • 列表项有编辑图标 → 可能需要编辑/更新接口
    • 有扫码图标 → 可能需要扫码查询接口

C. 对接与校准:

  1. 找到接口后,更新 Api.kt 中的占位路径为真实路径
  2. 根据接口的请求参数和返回字段结构,校准 Bean 类的字段名和类型
  3. 遵循 memory 中的 API 搜索原则:不跨模块混用接口,遇到不确定的接口询问用户确认

第 14 步:重新编译验证

对接真实 API 后重新编译,确保无错误:

./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 导入等)