feat: 国际出港查询详情

This commit is contained in:
2025-12-04 00:10:08 +08:00
parent 829a6328aa
commit 247b72b7e8
17 changed files with 1088 additions and 52 deletions

338
CLAUDE.md
View File

@@ -811,11 +811,269 @@ grep "custom_text" module_base/src/main/res/values/strings.xml
- **颜色**: `white`, `black`, `colorPrimary`, `text_normal`, `text_gray`, `text_red`
- **文字**: 优先直接写中文字符串,少用 string 资源
#### 10. DataBinding中View类未导入
```xml
<!-- ❌ 错误: 使用View.VISIBLE但未导入View -->
<data>
<variable name="viewModel" type="..." />
</data>
<View android:visibility="@{viewModel.isVisible ? View.VISIBLE : View.GONE}" />
<!-- ✅ 正确: 必须导入View类 -->
<data>
<import type="android.view.View" />
<variable name="viewModel" type="..." />
</data>
<View android:visibility="@{viewModel.isVisible ? View.VISIBLE : View.GONE}" />
```
#### 11. DataBinding中textStyle属性错误
```xml
<!-- ❌ 错误: textStyle不支持DataBinding字符串 -->
<TextView
android:textStyle="@{viewModel.isBold ? `bold` : `normal`}" />
<!-- ✅ 正确: 直接使用固定值或删除该属性 -->
<TextView
android:textStyle="bold" />
```
**原因**: `textStyle`属性只接受整数值(如`Typeface.BOLD`),不接受字符串
### 常用Import路径速查表
#### ⚠️ 重要提醒
在创建新的Activity、ViewModel、Fragment时,以下import路径**最容易出错**。务必使用正确的包名:
#### 基础类Import (module_base)
```kotlin
// ==================== 基类 ====================
import com.lukouguoji.module_base.base.BaseActivity // Activity基类
import com.lukouguoji.module_base.base.BaseBindingActivity // DataBinding Activity基类
import com.lukouguoji.module_base.base.BaseViewModel // ViewModel基类 ⚠️ 不是service.viewModel!
import com.lukouguoji.module_base.base.BasePageViewModel // 分页列表ViewModel基类
import com.lukouguoji.module_base.base.BaseFragment // Fragment基类
import com.lukouguoji.module_base.base.BaseBindingFragment // DataBinding Fragment基类
import com.lukouguoji.module_base.base.BaseViewHolder // ViewHolder基类
import com.lukouguoji.module_base.base.CustomVP2Adapter // ViewPager2适配器
// ==================== 常量类 ====================
import com.lukouguoji.module_base.common.Constant // 常量类 ⚠️ 不是根包下的Constant!
import com.lukouguoji.module_base.common.DetailsPageType // 详情页类型(Add/Modify/Details)
import com.lukouguoji.module_base.common.ConstantEvent // 事件常量
// ==================== 网络相关 ====================
import com.lukouguoji.module_base.http.net.NetApply // API调用入口
import com.lukouguoji.module_base.http.net.Api // API接口定义
import com.lukouguoji.module_base.bean.BaseResultBean // 通用返回结果
import com.lukouguoji.module_base.bean.BaseListBean // 列表返回结果
import com.lukouguoji.module_base.bean.PageInfo // 分页信息
// ==================== Kotlin扩展函数 ====================
import com.lukouguoji.module_base.ktx.launchCollect // 协程扩展(无Loading)
import com.lukouguoji.module_base.ktx.launchLoadingCollect // 协程扩展(带Loading)
import com.lukouguoji.module_base.ktx.showToast // Toast扩展
import com.lukouguoji.module_base.ktx.toRequestBody // Map转RequestBody ⚠️ 不是ext包!
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty // 非空验证
import com.lukouguoji.module_base.ktx.noNull // 空值处理
import com.lukouguoji.module_base.ktx.formatDate // 日期格式化
// ==================== 事件总线 ====================
import com.lukouguoji.module_base.impl.FlowBus // FlowBus事件总线
import com.lukouguoji.module_base.impl.observe // FlowBus观察扩展 ⚠️ 必须单独导入!
// ==================== 接口 ====================
import com.lukouguoji.module_base.interfaces.IOnItemClickListener // 列表项点击接口 ⚠️ 不是impl包!
// ==================== 路由 ====================
import com.lukouguoji.module_base.router.ARouterConstants // 路由常量
import com.alibaba.android.arouter.facade.annotation.Route // ARouter注解
// ==================== UI组件 ====================
import com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew // 数据展示组件
import com.lukouguoji.module_base.ui.weight.data.layout.DataLayoutType // 数据组件类型
import com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout // 搜索组件
import com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType // 搜索组件类型
```
#### Android标准库Import
```kotlin
// ==================== Activity & Fragment ====================
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
// ==================== Lifecycle & ViewModel ====================
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
// ==================== DataBinding ====================
import androidx.databinding.DataBindingUtil
// ==================== ViewPager2 ====================
import androidx.viewpager2.widget.ViewPager2
// ==================== Coroutines ====================
import kotlinx.coroutines.launch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
// ==================== View ====================
import android.view.View
import android.view.LayoutInflater
import android.view.ViewGroup
```
#### 业务模块Bean Import示例
```kotlin
// 国际出港模块
import com.lukouguoji.module_base.bean.GjcMaWb // 国际出港主单
import com.lukouguoji.module_base.bean.GjcHaWb // 国际出港分单
import com.lukouguoji.module_base.bean.GjcStorageUse // 库位使用
// 国内出港模块
import com.lukouguoji.module_base.bean.GncMaWb // 国内出港主单
```
#### 常见错误对照表
| ❌ 错误写法 | ✅ 正确写法 | 说明 |
|------------|------------|------|
| `com.lukouguoji.module_base.Constant` | `com.lukouguoji.module_base.common.Constant` | Constant在common包下 |
| `com.lukouguoji.module_base.service.viewModel.BaseViewModel` | `com.lukouguoji.module_base.base.BaseViewModel` | BaseViewModel在base包下 |
| `com.lukouguoji.module_base.ext.toRequestBody` | `com.lukouguoji.module_base.ktx.toRequestBody` | 扩展函数在ktx包下 |
| `com.lukouguoji.module_base.impl.IOnItemClickListener` | `com.lukouguoji.module_base.interfaces.IOnItemClickListener` | 接口在interfaces包下 |
| `com.lukouguoji.module_base.constant.DetailsPageType` | `com.lukouguoji.module_base.common.DetailsPageType` | 枚举在common包下 |
#### 快速查找正确Import路径
```bash
# 查找类的完整路径
find module_base/src/main/java -name "Constant.kt"
find module_base/src/main/java -name "BaseViewModel.kt"
# 查找函数定义位置
grep -r "fun.*toRequestBody" module_base/src/main/java --include="*.kt"
grep -r "class BaseViewModel" module_base/src/main/java --include="*.kt"
# 查找接口定义
find module_base/src/main/java -name "IOnItemClickListener.kt"
```
#### Activity/ViewModel/Fragment模板
**ViewModel模板 (带正确import):**
```kotlin
package com.lukouguoji.xxx.viewModel
import android.content.Intent
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.module_base.base.BaseViewModel // ⚠️ 正确路径
import com.lukouguoji.module_base.common.Constant // ⚠️ 正确路径
import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.ktx.launchLoadingCollect
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.ktx.toRequestBody // ⚠️ 正确路径
class XxxViewModel : BaseViewModel() {
val data = MutableLiveData<Any>()
fun loadData() {
val params = mapOf("key" to "value").toRequestBody()
launchLoadingCollect({ NetApply.api.getXxx(params) }) {
onSuccess = { data.value = it.data }
}
}
}
```
**Activity模板 (带正确import):**
```kotlin
package com.lukouguoji.xxx.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.xxx.R
import com.lukouguoji.xxx.databinding.ActivityXxxBinding
import com.lukouguoji.xxx.viewModel.XxxViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant // ⚠️ 正确路径
import com.lukouguoji.module_base.router.ARouterConstants
@Route(path = ARouterConstants.ACTIVITY_URL_XXX)
class XxxActivity : BaseBindingActivity<ActivityXxxBinding, XxxViewModel>() {
override fun layoutId() = R.layout.activity_xxx
override fun viewModelClass() = XxxViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("标题")
binding.viewModel = viewModel
}
}
```
**Fragment模板 (带正确import):**
```kotlin
package com.lukouguoji.xxx.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import com.lukouguoji.xxx.R
import com.lukouguoji.xxx.databinding.FragmentXxxBinding
import com.lukouguoji.xxx.viewModel.XxxViewModel
class XxxFragment : Fragment() {
private lateinit var binding: FragmentXxxBinding
private lateinit var viewModel: XxxViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_xxx,
container,
false
)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
return binding.root
}
companion object {
@JvmStatic
fun newInstance(vm: XxxViewModel) =
XxxFragment().apply { viewModel = vm }
}
}
```
### 错误排查流程
1. **资源引用错误** → 检查drawable/color/string是否存在,主动创建缺失资源
2. **DataBinding错误** → 检查import包名、枚举值
3. **Unresolved reference** → 检查import语句、常量定义
1. **Import错误 (Unresolved reference)** → 参考上方"常用Import路径速查表",使用正确包名
2. **资源引用错误** → 检查drawable/color/string是否存在,主动创建缺失资源
3. **DataBinding错误** → 检查import包名、枚举值、是否导入View类
4. **suspend function错误** → 在`viewModelScope.launch`中调用
5. **仍有问题**`./gradlew clean` 后重新构建
@@ -832,6 +1090,79 @@ find module_base/src -name "IOnItemClickListener.kt"
grep -A 5 "enum class DataLayoutType" module_base/src --include="*.kt"
```
### 标题栏统一规范
**重要规则**: 所有 Activity 布局必须使用统一的 title_tool_bar 组件,禁止手动编写 Toolbar。
#### 正确做法
**布局文件** (`activity_xxx.xml`):
```xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 标题栏 -->
<include layout="@layout/title_tool_bar" />
<!-- 其他内容 -->
...
</LinearLayout>
```
**Activity 文件** (`XxxActivity.kt`):
```kotlin
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("页面标题") // 自动设置标题和返回事件
binding.viewModel = viewModel
// 其他初始化...
}
```
#### 错误做法
**不要手动编写 Toolbar**:
```xml
<!-- 错误:手动配置 Toolbar -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/colorPrimary">
<LinearLayout android:id="@+id/tool_back">...</LinearLayout>
<TextView android:id="@+id/title_name">...</TextView>
</androidx.appcompat.widget.Toolbar>
```
**不要手动查找 tool_back 并设置点击事件**:
```kotlin
// 错误:手动处理返回按钮
binding.root.findViewById<LinearLayout>(R.id.tool_back)?.setOnClickListener {
finish()
}
```
#### title_tool_bar 工作原理
`title_tool_bar.xml` 包含三个关键 ID:
- `toolbar` - BaseBindingActivity 自动查找
- `tool_back` - 自动绑定 finish() 点击事件
- `title_name` - 通过 `setBackArrow()` 设置文字
**优点**:
- 统一视觉风格
- 减少重复代码
- 自动处理返回逻辑
- 维护简单 (修改一处,全局生效)
#### 参考示例
- `module_gjc/src/main/res/layout/activity_gjc_inspection.xml` - 第21行
- `module_gjc/src/main/res/layout/activity_gjc_box_weighing_details.xml` - 第21行
- `module_gjc/src/main/res/layout/activity_gjc_query_details.xml` - 第19行
### 布局最佳实践参考
参考以下文件进行布局设计:
@@ -843,6 +1174,7 @@ grep -A 5 "enum class DataLayoutType" module_base/src --include="*.kt"
### 开发原则
-**资源引用必须存在** - 创建/修改布局前,确保drawable/color/string资源真实存在或主动创建
-**标题栏统一使用 title_tool_bar** - 禁止手动编写 Toolbar,必须使用 `<include layout="@layout/title_tool_bar" />`,Activity 中调用 `setBackArrow("标题")`
-**必须设置 lifecycleOwner** - Activity 中 `binding.lifecycleOwner = this`(BaseBindingActivity 已自动设置)
-**新建Activity后必须在AndroidManifest.xml中注册**
- ✅ 优先使用项目现有基类和封装