diff --git a/.claude/commands/build-debug.md b/.claude/commands/build-debug.md new file mode 100644 index 0000000..4840262 --- /dev/null +++ b/.claude/commands/build-debug.md @@ -0,0 +1,3 @@ +构建项目的 Debug 版本 APK + +./gradlew assembleDebug diff --git a/.claude/commands/build-release.md b/.claude/commands/build-release.md new file mode 100644 index 0000000..3bd40a2 --- /dev/null +++ b/.claude/commands/build-release.md @@ -0,0 +1,3 @@ +构建项目的 Release 版本 APK (已签名) + +./gradlew assembleRelease diff --git a/.claude/commands/check-modules.md b/.claude/commands/check-modules.md new file mode 100644 index 0000000..a9bcc0d --- /dev/null +++ b/.claude/commands/check-modules.md @@ -0,0 +1,6 @@ +显示项目中所有模块的列表和基本信息 + +echo "=== 项目模块列表 ===" && \ +cat settings.gradle | grep "include" && \ +echo "\n=== 模块目录 ===" && \ +ls -la | grep "^d" | grep "module_" diff --git a/.claude/commands/clean-build.md b/.claude/commands/clean-build.md new file mode 100644 index 0000000..b3e118e --- /dev/null +++ b/.claude/commands/clean-build.md @@ -0,0 +1,3 @@ +清理构建缓存并重新构建 Debug 版本 + +./gradlew clean && ./gradlew assembleDebug diff --git a/.claude/commands/devices.md b/.claude/commands/devices.md new file mode 100644 index 0000000..07eff27 --- /dev/null +++ b/.claude/commands/devices.md @@ -0,0 +1,3 @@ +列出所有已连接的 Android 设备和模拟器 + +adb devices -l diff --git a/.claude/commands/install.md b/.claude/commands/install.md new file mode 100644 index 0000000..c742e6f --- /dev/null +++ b/.claude/commands/install.md @@ -0,0 +1,3 @@ +构建并安装 Debug 版本到连接的 Android 设备或模拟器 + +./gradlew installDebug diff --git a/.claude/commands/lint.md b/.claude/commands/lint.md new file mode 100644 index 0000000..ebaad22 --- /dev/null +++ b/.claude/commands/lint.md @@ -0,0 +1,3 @@ +运行 Android Lint 代码质量检查 + +./gradlew lint diff --git a/.claude/commands/logs.md b/.claude/commands/logs.md new file mode 100644 index 0000000..9628855 --- /dev/null +++ b/.claude/commands/logs.md @@ -0,0 +1,3 @@ +实时查看应用日志 (过滤应用包名) + +adb logcat | grep "com.lukouguoji.aerologic" diff --git a/.claude/settings.local.json b/.claude/settings.local.json index f67c6be..ce47dc4 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -24,7 +24,14 @@ "WebSearch", "Bash(chmod:*)", "Bash(xargs ls:*)", - "Bash(xargs rm -rf)" + "Bash(xargs rm -rf)", + "Bash(for module in module_gnc module_gnj module_gjc module_gjj module_hangban module_cargo module_mit module_p Printer MPChartLib)", + "Bash(do echo '=== $module ===' ls -la /Users/kid/Development/Fusion/Projects/aerologic-app/$module/)", + "Bash(git -C /Users/kid/Development/Fusion/Projects/aerologic-app log --oneline)", + "Bash(git -C /Users/kid/Development/Fusion/Projects/aerologic-app branch -a)", + "Bash(adb:*)", + "Bash(emulator:*)", + "Bash(logcat:*)" ], "deny": [], "ask": [] diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c20f070 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,101 @@ +# 版本变更日志 (Changelog) + +本文档记录 AirLogistics 项目的所有重要变更。 + +格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), +版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。 + +## [1.8.4] - 2024-12-02 + +### 新增 (Added) +- 完善 Claude Code 配置,添加 Android 开发权限 +- 创建 8 个快捷命令 (/build-debug, /install, /logs 等) +- 定制 CLAUDE.md 为 Android 项目专用开发指南 +- 添加 CONTRIBUTING.md 贡献指南文档 +- 添加 CHANGELOG.md 版本变更日志 + +### 改进 (Changed) +- 优化首页菜单交互体验 +- 改进 UI 界面显示效果 + +### 技术栈 +- Kotlin 1.6.21 +- Android Gradle Plugin 7.2.0 +- Gradle 7.3.3 +- minSdk 24, targetSdk 30, compileSdk 31 + +--- + +## [1.8.x] - 之前版本 + +### 主要功能 +- 国内出港业务模块 (货物收运、复磅称重、转运管理、出库装机) +- 国内进港业务模块 (舱单管理、卸机入库、出库提货、移库管理) +- 国际出港业务模块 (国际货物收运、板箱组装、ULD容器管理) +- 国际进港业务模块 (国际舱单管理、报文解析、理货管理) +- 航班管理模块 (航班查询、航班统计) +- 货物追踪模块 (货物状态追踪、运输日志) +- 监装监卸模块 (监装监卸管理) +- PDA功能模块 (PDA专用功能) +- 蓝牙打印模块 (佳博打印机集成) + +### 核心架构 +- MVVM 架构模式 +- 组件化模块设计 (11个业务模块 + 1个基础库) +- DataBinding 双向绑定 +- Kotlin Coroutines + Flow 异步处理 +- ARouter 模块间通信 +- Retrofit + OkHttp 网络请求 +- Glide 图片加载 +- MPAndroidChart 图表展示 + +### 开发基础设施 +- BaseActivity/BaseBindingActivity 基类 +- BaseViewModel/BasePageViewModel 基类 +- CommonAdapter + BaseViewHolder 列表适配 +- PadSearchLayout/PadDataLayout 自定义组件 +- 完整的 Kotlin 扩展函数库 +- DataBinding 适配器集合 + +--- + +## 版本说明 + +### 版本号规则 +- **Major.Minor.Patch** (例: 1.8.4) +- **Major**: 重大架构变更或不兼容的 API 修改 +- **Minor**: 新功能添加,向下兼容 +- **Patch**: Bug 修复和小的改进 + +### 变更类型 +- **Added**: 新增功能 +- **Changed**: 功能改进或变更 +- **Deprecated**: 即将废弃的功能 +- **Removed**: 已移除的功能 +- **Fixed**: Bug 修复 +- **Security**: 安全性修复 + +--- + +## 未来计划 + +### 待开发功能 +- [ ] 优化数据同步机制 +- [ ] 增强离线模式支持 +- [ ] 改进用户权限管理 +- [ ] 优化蓝牙打印稳定性 +- [ ] 添加数据导出功能 +- [ ] 性能监控和分析 + +### 技术改进 +- [ ] 升级到 Kotlin 2.0 +- [ ] 迁移到 Jetpack Compose +- [ ] 优化构建速度 +- [ ] 完善单元测试覆盖 +- [ ] 添加 UI 自动化测试 + +--- + +**维护**: 本文档应在每次版本发布时更新。 +**负责人**: 项目维护团队 +**最后更新**: 2024-12-02 diff --git a/CLAUDE.md b/CLAUDE.md index 780bb74..4a08f9b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,857 +1,965 @@ -# CLAUDE.md - -项目开发指南 - 航空物流App - -## 项目概述 - -**AirLogistics** - Android原生应用,航空物流全流程管理 - -- **包名**: com.lukouguoji.aerologic -- **版本**: 1.7.9 (API 24-30) -- **架构**: MVVM + 组件化 + Kotlin + DataBinding -- **屏幕**: 横屏 1152dp × 720dp - -## 快速构建 - -```bash -./gradlew assembleDebug # 构建Debug版本 -./gradlew clean # 清理构建 -``` - -## 核心架构 - -### MVVM层级 - -``` -Activity → BaseBindingActivity → ViewModel → BaseViewModel/BasePageViewModel → API -``` - -### 关键基类 - -- **BaseBindingActivity**: DataBinding + ViewModel自动绑定 -- **BaseViewModel**: Loading管理、协程支持 -- **BasePageViewModel**: 分页列表(含PageModel) -- **CommonAdapter + BaseViewHolder**: 列表适配器 -- **PadSearchLayout**: 搜索区域输入控件 -- **PadDataLayout**: 数据展示/编辑控件 - -### 标准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 - // 初始化UI - } -} -``` - -### 标准ViewModel模板 - -**列表页ViewModel:** - -```kotlin -class XxxListViewModel : BasePageViewModel() { - val searchText = MutableLiveData() - val itemLayoutId = R.layout.item_xxx - val itemViewHolder = XxxViewHolder::class.java - - override fun getData() { - val params = mapOf( - "page" to pageModel.page, - "limit" to pageModel.limit, - "searchText" to searchText.value - ).toRequestBody() - - launchLoadingCollect({ NetApply.api.getXxxList(params) }) { - onSuccess = { pageModel.handleListBean(it) } - } - } - - override fun onItemClick(position: Int, type: Int) { - val bean = pageModel.rv!!.commonAdapter()!!.getItem(position) as XxxBean - // 跳转详情 - } -} -``` - -**详情页ViewModel:** - -```kotlin -class XxxDetailsViewModel : BaseViewModel() { - var id = "" - val dataBean = MutableLiveData() - - fun initOnCreated(intent: Intent) { - id = intent.getStringExtra(Constant.Key.ID) ?: "" - getData() - } - - private fun getData() { - launchLoadingCollect({ NetApply.api.getXxxDetails(id) }) { - onSuccess = { dataBean.value = it.data ?: XxxBean() } - } - } -} -``` - -**编辑页ViewModel:** - -```kotlin -class XxxAddViewModel : BaseViewModel() { - val pageType = MutableLiveData(DetailsPageType.Add) // 必须用LiveData - var id = "" - val dataBean = MutableLiveData(XxxBean()) - - fun initOnCreated(intent: Intent) { - pageType.value = DetailsPageType.valueOf( - intent.getStringExtra(Constant.Key.PAGE_TYPE) ?: DetailsPageType.Add.name - ) - if (pageType.value != DetailsPageType.Add) { - id = intent.getStringExtra(Constant.Key.ID) ?: "" - loadData() - } - } - - fun submit() { - val bean = dataBean.value ?: return - if (bean.name.verifyNullOrEmpty("请输入名称")) return - - launchLoadingCollect({ - val params = mapOf("id" to id, "name" to bean.name) - .toRequestBody(removeEmptyOrNull = true) - NetApply.api.saveXxx(params) - }) { - onSuccess = { - showToast("保存成功") - viewModelScope.launch { - FlowBus.with(ConstantEvent.EVENT_REFRESH).emit("refresh") - } - getTopActivity().finish() - } - } - } -} -``` - -## 网络请求 - -### 请求方法 - -```kotlin -// 带Loading请求 -launchLoadingCollect({ NetApply.api.saveXxx(params) }) { - onSuccess = { /* 成功处理 */ } - onFailed = { code, msg -> /* 失败处理 */ } -} - -// 无Loading请求(后台刷新) -launchCollect({ NetApply.api.getXxx() }) { - onSuccess = { /* 成功处理 */ } -} - -// 参数转换 -val params = mapOf("key" to "value").toRequestBody(removeEmptyOrNull = true) -``` - -### API接口定义 - -```kotlin -// 位置: module_base/.../http/net/Api.kt -@POST("api/xxx/list") -suspend fun getXxxList(@Body data: RequestBody): BaseListBean - -@POST("api/xxx/details") -suspend fun getXxxDetails(@Query("id") id: String): BaseResultBean - -@POST("api/xxx/save") -suspend fun saveXxx(@Body data: RequestBody): BaseResultBean -``` - -## DataBinding + LiveData + ViewModel 核心知识 - -### 🎯 最关键的设置(最常见错误) - -**必须在 Activity 中设置 lifecycleOwner,否则 XML 中的 LiveData 不会自动更新 UI!** - -```kotlin -override fun initOnCreate(savedInstanceState: Bundle?) { - setBackArrow("页面标题") - binding.viewModel = viewModel - - // ⚠️ 关键:必须设置,否则 LiveData 无法自动更新 UI - binding.lifecycleOwner = this -} -``` - -**BaseBindingActivity 已自动设置**,但如果手动使用 DataBinding 时务必记住! - -### 📖 ViewModel 中 LiveData 的定义规范 - -```kotlin -class XxxViewModel : BaseViewModel() { - // ✅ 推荐:对外暴露不可变的 LiveData - private val _dataBean = MutableLiveData() - val dataBean: LiveData = _dataBean - - // ✅ 简化写法:直接使用 MutableLiveData(项目常用) - val searchText = MutableLiveData() - val pageType = MutableLiveData(DetailsPageType.Add) - - fun loadData() { - // 主线程更新 - _dataBean.value = XxxBean() - - // 子线程更新(协程中不需要,已在主线程) - // _dataBean.postValue(XxxBean()) - } -} -``` - -### 📝 XML 中 LiveData 的绑定方式 - -#### 1. 单向绑定 `@{}`(只显示,ViewModel → UI) - -```xml - - - - - - - - - - - - - - - - - -``` - -#### 2. 双向绑定 `@={}`(可编辑,UI ↔ ViewModel) - -```xml - - - - - - - - -``` - -**双向绑定要求**: -- 字段必须是 `MutableLiveData` -- 用户输入时自动更新 ViewModel 的值 -- ViewModel 更新值时自动更新 UI - -#### 3. 点击事件绑定 - -```xml - -