Compare commits

...

94 Commits

Author SHA1 Message Date
c6fe8f5310 feat: 开始组装 opt 2025-12-19 11:42:21 +08:00
b8fbc304f1 feat: opt 开始装载 2025-12-18 16:34:06 +08:00
3df9a9f73f feat: opt 开始装载 2025-12-18 15:28:37 +08:00
222ae8a0f5 feat: opt 开始组装 tree 2025-12-18 11:34:06 +08:00
1906e7ce65 feat: 出港组装 开始组装 status color 2025-12-18 10:59:03 +08:00
01306d1302 feat: 出港组装 开始组装 卸货、装货 api 2025-12-18 10:10:04 +08:00
e208d54a3c feat: 开始组装 opt 2025-12-18 09:52:13 +08:00
2664cc7c69 feat: 开始组装 opt 2025-12-16 15:58:37 +08:00
644c937476 feat: 开始组装 2025-12-16 15:36:13 +08:00
ced24685ee feat: opt 编码输入 2025-12-16 15:27:06 +08:00
fafe092b3c feat: opt 板箱过磅 print 2025-12-16 15:12:17 +08:00
eb1d356bd2 feat: opt 板箱过磅 auto calc 2025-12-16 14:07:11 +08:00
f3056d3807 feat: opt 板箱过磅 add 2025-12-16 11:00:44 +08:00
1eadb9043e feat: 国际出港 收运检查 退回 list 2025-12-16 10:31:54 +08:00
bd8d6f683a feat: 国际出港 收运检查 退回 2025-12-16 10:21:05 +08:00
1cfcb3fe97 feat: opt 出港计重 开始计重 floatButton 2025-12-16 10:10:42 +08:00
b4238a04d0 feat: opt 出港计重 计重明细 update 2025-12-16 09:36:36 +08:00
d1ed050c76 feat: opt 出港计重 完成计重 2025-12-16 09:17:20 +08:00
d600cbe4f1 feat: opt 出港计重 完成计重 2025-12-16 09:06:38 +08:00
20bab628ee feat: opt 出港组装 组装人 2025-12-15 17:47:02 +08:00
7fb91d29b2 feat: opt 收货交接单 2025-12-15 17:17:45 +08:00
746e4cce03 feat: opt handover ui 2025-12-15 17:11:08 +08:00
54ea9dbe75 feat: opt 货物交接单 2025-12-15 16:53:35 +08:00
7650a835f2 feat: opt ui 2025-12-15 15:59:12 +08:00
0531f5e0c4 feat: opt 出库交接 2025-12-15 14:25:11 +08:00
2fa567f2d2 feat: opt del reason 2025-12-15 14:10:08 +08:00
23185616d1 feat: 删除原因 2025-12-15 12:34:00 +08:00
5c7284e617 feat: opt 收运检查 列表 2025-12-15 11:09:57 +08:00
2721e36dc1 feat: auto login in dev 2025-12-15 10:38:30 +08:00
9129ccfb88 feat: opt 国际出港 出港运抵 item 2025-12-15 09:58:36 +08:00
fa79b7d9f3 feat: opt 国际出港 出港理货 item 2025-12-15 09:42:13 +08:00
77d79c4251 feat: 删除原因 dialog for 出港运抵/理货/装载 2025-12-12 13:41:01 +08:00
6b79433557 feat: 出港理货 2025-12-12 13:12:50 +08:00
d9fb950b79 feat: opt action icon 2025-12-12 12:15:20 +08:00
d1e54b540c feat: 出港装载 2025-12-12 11:51:47 +08:00
9e0cae4321 feat: opt 板箱过磅 list 2025-12-12 10:59:58 +08:00
38e336842a feat: opt 板箱过磅 list 2025-12-12 10:48:19 +08:00
48abc944f0 feat: opt layout 2025-12-12 10:39:45 +08:00
82fc593497 feat: 国际出港 组装分配 2025-12-11 19:06:06 +08:00
a81567f10b feat: 国际出港 出港组装 开始组装 2025-12-09 20:44:01 +08:00
7d39cbf70f feat: 国际出港 出港组装 开始组装 2025-12-09 17:58:25 +08:00
2871cbf784 feat: 开始组装 ui 2025-12-09 16:37:49 +08:00
249b5a4e87 feat: dev 2025-12-09 14:19:30 +08:00
6ebedc7366 feat: passageWay for api 2025-12-08 19:15:22 +08:00
a3b8746264 feat: 出港计重 已计重 2025-12-08 17:17:40 +08:00
424755298a feat: 开始计重 calc vol 2025-12-08 16:14:27 +08:00
ee2bfe02a7 feat: 开始计重 业务类型 2025-12-08 16:04:28 +08:00
37ffc539c9 feat: 国际出港 出港计重 分托 2025-12-08 15:48:32 +08:00
9149d1ad35 feat: 国际出港 开始计重 通道 2025-12-08 15:02:06 +08:00
5fc51e7af3 feat: 出港计重 开始计重 2025-12-08 14:14:47 +08:00
2be3cf5251 feat: 出港计重 优化时间显示 2025-12-08 11:26:26 +08:00
7752954d1b feat: 出港计重 特码 filter 2025-12-08 10:58:50 +08:00
ccd93a14bb feat: 出港计重 代理 filter 2025-12-08 10:45:11 +08:00
d401f849c8 feat: 出港计重 ui 2025-12-08 10:31:20 +08:00
09d04cf539 Merge branch 'main' of ssh://git.njcqit.com:2222/eric/aerologic-app 2025-12-06 15:43:05 +08:00
21e0857790 feat: 收运检查-交接单 2025-12-06 15:42:56 +08:00
f72f853fb7 feat: opt ui 2025-12-06 12:23:25 +08:00
f17b68a94c feat: ui opt v2 2025-12-05 13:05:55 +08:00
d5bc14f0f1 feat: ui opt 2025-12-05 12:55:32 +08:00
bf1d897044 feat: opt ui 2025-12-05 12:42:57 +08:00
e1e16fbd9e feat: ui opt 2025-12-05 11:54:49 +08:00
c0c19c8453 feat: opt 出港查询 ui 2025-12-05 11:41:08 +08:00
bcc4bada25 feat: ui opt 2025-12-05 11:18:11 +08:00
6073010531 feat: 出港运抵 2025-12-04 18:59:54 +08:00
d67c5edb19 feat: update claude md file 2025-12-04 17:35:03 +08:00
7f7ffcccac feat: 出库交接 2025-12-04 17:17:01 +08:00
22adce2964 feat: fix api define 2025-12-04 16:45:10 +08:00
6f89ad0520 feat: dev 2025-12-04 16:07:51 +08:00
266fafb898 feat: 国际出港组装分配 2025-12-04 14:12:30 +08:00
18e6258a40 feat: 国际出港查询详情 fix 2025-12-04 12:45:15 +08:00
0723f7382e feat: 国际出港查询详情 tab 库位信息 2025-12-04 12:38:50 +08:00
8e1d716159 feat: 国际出港查询详情 tab 仓库信息 2025-12-04 12:13:59 +08:00
08f814531c feat: 国际出港查询详情 opt 2025-12-04 10:46:49 +08:00
247b72b7e8 feat: 国际出港查询详情 2025-12-04 00:10:08 +08:00
829a6328aa feat: 国际出港运单修改 2025-12-03 21:38:50 +08:00
f1abc3ddfc feat: opt GjcQueryActivity list 2025-12-03 19:56:31 +08:00
be0541f522 feat: 国际出港查询 2025-12-02 18:02:17 +08:00
0c3c78b42e feat: power claude templates 2025-12-02 16:21:19 +08:00
ea6b2f08a7 feat: opt home menu 2025-12-02 15:51:29 +08:00
35a9ee145a feat: ui 2025-11-27 21:13:22 +08:00
f797767919 feat: ui 2025-11-27 18:07:14 +08:00
d69f964464 feat: io 2025-11-27 17:49:14 +08:00
0fbb9c3704 feat: io 2025-11-27 17:17:17 +08:00
29b1875fa5 feat: 出港组装 list 2025-11-27 13:11:33 +08:00
bea8a8c8c8 feat: ui opt 2025-11-27 11:28:50 +08:00
7e80d0e789 feat: opt ui 2025-11-26 22:46:21 +08:00
83ccad4171 feat: ui opt 2025-11-26 19:39:05 +08:00
ec9818e267 feat: ui opt 2025-11-26 18:08:02 +08:00
c002f1172a feat: ui opt 2025-11-26 18:00:36 +08:00
4a3b8a85eb feat: ui opt 2025-11-26 17:56:00 +08:00
a01b17dd8f feat: opt layout ui 2025-11-26 11:52:40 +08:00
8a30f0079a feat: update claude md file 2025-11-26 09:39:20 +08:00
938f7dca32 Merge branch 'main' of ssh://git.njcqit.com:2222/eric/aerologic-app 2025-11-24 15:05:52 +08:00
9bb4a5efb3 feat: 出港计重 ui v3 2025-11-24 15:05:36 +08:00
198 changed files with 21182 additions and 3029 deletions

View File

@@ -0,0 +1,3 @@
构建项目的 Debug 版本 APK
./gradlew assembleDebug

View File

@@ -0,0 +1,3 @@
构建项目的 Release 版本 APK (已签名)
./gradlew assembleRelease

View File

@@ -0,0 +1,6 @@
显示项目中所有模块的列表和基本信息
echo "=== 项目模块列表 ===" && \
cat settings.gradle | grep "include" && \
echo "\n=== 模块目录 ===" && \
ls -la | grep "^d" | grep "module_"

View File

@@ -0,0 +1,3 @@
清理构建缓存并重新构建 Debug 版本
./gradlew clean && ./gradlew assembleDebug

View File

@@ -0,0 +1,3 @@
列出所有已连接的 Android 设备和模拟器
adb devices -l

View File

@@ -0,0 +1,3 @@
构建并安装 Debug 版本到连接的 Android 设备或模拟器
./gradlew installDebug

3
.claude/commands/lint.md Normal file
View File

@@ -0,0 +1,3 @@
运行 Android Lint 代码质量检查
./gradlew lint

3
.claude/commands/logs.md Normal file
View File

@@ -0,0 +1,3 @@
实时查看应用日志 (过滤应用包名)
adb logcat | grep "com.lukouguoji.aerologic"

View File

@@ -20,7 +20,24 @@
"Bash(xargs -I {} sh -c 'echo \"\"\"\"=== {} ===\"\"\"\" && jar tf {} 2>/dev/null | grep -i \"\"\"\"gprinter\"\"\"\" | head -5')",
"Bash(xmllint:*)",
"Bash(xargs cat:*)",
"mcp__chrome-devtools__evaluate_script"
"mcp__chrome-devtools__evaluate_script",
"WebSearch",
"Bash(chmod:*)",
"Bash(xargs ls:*)",
"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:*)",
"Bash(grep:*)",
"Bash(sort:*)",
"Bash(ls:*)",
"Bash(xargs rm:*)",
"Bash(git -C /Users/kid/Development/Fusion/Projects/aerologic-app stash)",
"WebFetch(domain:api.apifox.cn)"
],
"deny": [],
"ask": []

View File

@@ -0,0 +1,246 @@
# 大写字母数字输入过滤器使用指南
## 功能说明
为 EditText 提供输入过滤功能:
- ✅ 只允许输入大写字母(A-Z)和数字(0-9)
- ✅ 小写字母自动转为大写
- ✅ 自动过滤中文、特殊符号、空格等所有非法字符
## 核心实现
**位置**: `module_base/src/main/java/com/lukouguoji/module_base/ktx/EditTextKtx.kt`
```kotlin
/**
* 大写字母和数字输入过滤器
*/
class UpperCaseAlphanumericInputFilter : InputFilter {
// 实现细节...
}
/**
* 为 EditText 设置大写字母和数字输入过滤器
*/
fun EditText.setUpperCaseAlphanumericFilter() {
this.filters = arrayOf(UpperCaseAlphanumericInputFilter())
}
```
## 使用方式
### 方式一:直接为 EditText 设置(最简单)
```kotlin
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
// Activity 中
override fun initOnCreate(savedInstanceState: Bundle?) {
// 为普通 EditText 设置
binding.editText.setUpperCaseAlphanumericFilter()
}
```
### 方式二:为 PadDataLayoutNew 内部的 EditText 设置
```kotlin
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
// Activity 中
override fun initOnCreate(savedInstanceState: Bundle?) {
// PadDataLayoutNew 内部有一个 et 属性,是 EditText
binding.carIdInput.et.setUpperCaseAlphanumericFilter()
binding.uldNoInput.et.setUpperCaseAlphanumericFilter()
binding.impCodeInput.et.setUpperCaseAlphanumericFilter()
}
```
### 方式三:在自定义 View 中设置
```kotlin
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
class CustomView : FrameLayout {
private val editText: EditText
init {
// 初始化后设置
editText.setUpperCaseAlphanumericFilter()
}
}
```
## 实际应用案例
### 案例1板箱过磅页面GjcBoxWeighingAddActivity
**需求**: 架子车号、ULD编码、IMP代码只允许输入大写字母和数字
**实现**:
```kotlin
// GjcBoxWeighingAddActivity.kt
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("板箱过磅")
binding.viewModel = viewModel
viewModel.initOnCreated(this)
// 为架子车号、ULD编码、IMP代码添加大写字母和数字的输入限制
binding.carIdInput.et.setUpperCaseAlphanumericFilter()
binding.uldNoInput.et.setUpperCaseAlphanumericFilter()
binding.impCodeInput.et.setUpperCaseAlphanumericFilter()
}
```
**效果**:
- 输入 `abc123` → 自动变成 `ABC123`
- 输入 `板箱_88` → 自动变成 `88`
- 输入 `test@#456` → 自动变成 `TEST456`
### 案例2其他可能需要的场景
**航班号输入框**:
```kotlin
binding.flightNoInput.et.setUpperCaseAlphanumericFilter()
```
**运单号输入框**:
```kotlin
binding.waybillNoInput.et.setUpperCaseAlphanumericFilter()
```
**任何需要大写字母+数字的输入框**:
```kotlin
binding.anyInput.et.setUpperCaseAlphanumericFilter()
```
## 注意事项
### 1. Import 路径
```kotlin
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
```
### 2. 调用时机
必须在 View 初始化完成后调用,通常在 Activity 的 `initOnCreate` 或 Fragment 的 `onViewCreated` 中。
### 3. 与其他 Filter 组合使用
如果需要同时使用多个 Filter
```kotlin
val filters = arrayOf(
UpperCaseAlphanumericInputFilter(),
InputFilter.LengthFilter(20) // 同时限制长度
)
editText.filters = filters
```
### 4. 覆盖已有 Filter
调用 `setUpperCaseAlphanumericFilter()` 会覆盖已有的 Filter。如果需要保留原有 Filter请手动组合
```kotlin
val existingFilters = editText.filters
val newFilters = existingFilters + UpperCaseAlphanumericInputFilter()
editText.filters = newFilters
```
## 扩展建议
如果需要其他类型的输入过滤器,可以参考 `UpperCaseAlphanumericInputFilter` 的实现:
### 示例1只允许数字
```kotlin
class DigitsOnlyInputFilter : InputFilter {
override fun filter(
source: CharSequence?,
start: Int,
end: Int,
dest: Spanned?,
dstart: Int,
dend: Int
): CharSequence? {
if (source.isNullOrEmpty()) return null
val filtered = StringBuilder()
for (i in start until end) {
val char = source[i]
if (char in '0'..'9') {
filtered.append(char)
}
}
return if (filtered.toString() == source.subSequence(start, end).toString()) {
null
} else {
filtered.toString()
}
}
}
fun EditText.setDigitsOnlyFilter() {
this.filters = arrayOf(DigitsOnlyInputFilter())
}
```
### 示例2只允许小写字母和数字
```kotlin
class LowerCaseAlphanumericInputFilter : InputFilter {
override fun filter(
source: CharSequence?,
start: Int,
end: Int,
dest: Spanned?,
dstart: Int,
dend: Int
): CharSequence? {
if (source.isNullOrEmpty()) return null
val filtered = StringBuilder()
for (i in start until end) {
val char = source[i]
if (char in 'A'..'Z' || char in 'a'..'z' || char in '0'..'9') {
filtered.append(char.lowercaseChar())
}
}
return if (filtered.toString() == source.subSequence(start, end).toString()) {
null
} else {
filtered.toString()
}
}
}
fun EditText.setLowerCaseAlphanumericFilter() {
this.filters = arrayOf(LowerCaseAlphanumericInputFilter())
}
```
## 测试建议
在使用前建议测试以下场景:
1. 输入小写字母是否自动转大写
2. 输入中文是否被过滤
3. 输入特殊符号是否被过滤
4. 输入空格是否被过滤
5. 粘贴文本是否正确处理
6. 与软键盘的兼容性
## 相关文件
- **核心实现**: `module_base/src/main/java/com/lukouguoji/module_base/ktx/EditTextKtx.kt`
- **使用示例**: `module_gjc/src/main/java/com/lukouguoji/gjc/activity/GjcBoxWeighingAddActivity.kt`
---
**最后更新**: 2025-12-16
**维护人员**: 开发团队

View File

@@ -4,6 +4,14 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-11-27T08:04:42.915090Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=80STHZCHDLL4311422" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>

101
CHANGELOG.md Normal file
View File

@@ -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

2659
CLAUDE.md

File diff suppressed because it is too large Load Diff

273
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,273 @@
# 贡献指南 (Contributing Guide)
感谢你对 AirLogistics 项目的关注!本文档将指导你如何参与项目开发。
## 开发环境搭建
### 前置要求
- **IDE**: Android Studio Arctic Fox (2020.3.1) 或更高版本
- **JDK**: JDK 1.8
- **Gradle**: 7.3.3 (使用项目内置的 Gradle Wrapper)
- **Kotlin**: 1.6.21 (已在项目中配置)
- **Android SDK**: API 24-31
### 克隆项目
```bash
git clone ssh://git@git.njcqit.com:2222/eric/aerologic-app.git
cd aerologic-app
```
### 配置 Android SDK
1. 创建 `local.properties` 文件 (如果不存在):
```properties
sdk.dir=/path/to/your/android/sdk
```
2. 在 Android Studio 中:
- 打开项目
- 等待 Gradle 同步完成
- 如提示 SDK 路径问题,在 Settings → Android SDK 中配置
### Gradle 依赖配置
项目已配置阿里云 Maven 镜像和私有仓库,正常情况下依赖会自动下载。
如遇依赖下载问题:
1. 下载 gradle-7.3.3-bin.zip
- 百度网盘: https://pan.baidu.com/s/18wsuGRlNxjMYbxLhBH9yeg
- 提取码: 1029
2. 配置 Gradle:
- Settings → Build, Execution, Deployment → Build Tools → Gradle
- 解压并替换到 "Gradle user home" 目录
## 项目构建
### 首次构建
```bash
# 清理并构建 Debug 版本
./gradlew clean assembleDebug
# 或使用快捷命令
/clean-build
```
### 常用构建命令
```bash
# 构建 Debug APK
./gradlew assembleDebug
# 或使用: /build-debug
# 构建 Release APK (已签名)
./gradlew assembleRelease
# 或使用: /build-release
# 安装到设备
./gradlew installDebug
# 或使用: /install
# 运行代码检查
./gradlew lint
# 或使用: /lint
# 查看已连接设备
adb devices -l
# 或使用: /devices
# 查看应用日志
adb logcat | grep "com.lukouguoji.aerologic"
# 或使用: /logs
```
### 组件化开发模式
项目支持模块独立运行,方便单模块调试:
1. 编辑 `gradle.properties`
2. 设置 `isBuildModule=true` (独立运行) 或 `false` (集成模式)
3. Sync 项目
4. 运行对应模块
**注意**: 切换模式后需要重新 Sync 项目。
## 分支策略
### 分支类型
- **main**: 主分支,用于发布稳定版本
- **feature/xxx**: 功能开发分支 (例: `feature/cargo-tracking`)
- **hotfix/xxx**: 紧急修复分支 (例: `hotfix/login-crash`)
- **refactor/xxx**: 重构分支 (例: `refactor/network-layer`)
### 分支命名规范
- 使用小写字母和连字符
- 功能分支: `feature/功能名称-简短描述`
- 修复分支: `hotfix/问题简述`
- 重构分支: `refactor/重构范围`
### 工作流程
1. 从 main 分支创建新分支:
```bash
git checkout main
git pull
git checkout -b feature/your-feature-name
```
2. 在新分支上开发并提交
3. 推送到远程仓库:
```bash
git push -u origin feature/your-feature-name
```
4. 创建 Pull Request,等待代码审查
## 提交规范
### Conventional Commits 格式
提交信息应遵循以下格式:
```
<类型>: <简短描述>
[可选的详细说明]
[可选的 footer]
```
### 提交类型
- **feat**: 新功能 (例: `feat: 添加国内出港货物复磅功能`)
- **fix**: Bug修复 (例: `fix: 修复登录页面崩溃问题`)
- **refactor**: 代码重构 (例: `refactor: 优化网络请求层架构`)
- **docs**: 文档更新 (例: `docs: 更新 README 安装说明`)
- **style**: 代码格式调整 (例: `style: 格式化 LoginActivity 代码`)
- **test**: 测试相关 (例: `test: 添加货物追踪单元测试`)
- **chore**: 构建/工具相关 (例: `chore: 升级 Kotlin 到 1.6.21`)
### 提交示例
```bash
# 好的提交信息
git commit -m "feat: 添加国际进港舱单管理功能"
git commit -m "fix: 修复蓝牙打印机连接失败问题"
git commit -m "refactor: 重构 BaseViewModel 生命周期管理"
# 不好的提交信息
git commit -m "update"
git commit -m "修改了一些文件"
git commit -m "功能完成"
```
## 代码审查流程
### 创建 Pull Request
1. 确保代码已推送到远程仓库
2. 在 Git 托管平台创建 Pull Request
3. 填写 PR 描述:
- 功能说明
- 变更内容
- 测试情况
- 相关 Issue (如有)
### 代码审查要点
审查者应关注:
- [ ] 代码是否符合项目架构和规范
- [ ] 是否使用了正确的基类 (BaseActivity, BaseViewModel等)
- [ ] DataBinding 和协程使用是否正确
- [ ] 命名是否清晰、符合约定
- [ ] 是否有明显的性能问题
- [ ] 是否有代码重复或可复用的逻辑
### 合并前检查清单
提交者在创建 PR 前应确保:
- [ ] 代码编译通过,无编译错误
- [ ] 遵循项目命名和文件组织规范
- [ ] 使用了项目提供的基类和工具类
- [ ] DataBinding 正确使用
- [ ] 协程和 Flow 使用规范
- [ ] 无明显的内存泄漏风险
- [ ] 已在真机或模拟器上测试
- [ ] 提交信息符合规范
## 开发注意事项
### 架构要求
1. **优先使用现有基类**
- Activity 继承 `BaseActivity` 或 `BaseBindingActivity`
- ViewModel 继承 `BaseViewModel` 或 `BasePageViewModel`
- 列表适配器使用 `CommonAdapter` + `BaseViewHolder`
2. **使用统一 UI 组件**
- 搜索输入: 使用 `PadSearchLayout`
- 数据展示: 使用 `PadDataLayout`
- 遵循全局样式规范 (colors, dimens, styles)
3. **网络请求规范**
- 使用 `launchCollect` 或 `launchLoadingCollect` 扩展函数
- API 定义在对应模块的 `api/` 目录下
- 数据模型继承 `BaseResultBean` 或 `BaseListBean`
### 代码风格
- 遵循 Kotlin 官方编码规范
- 使用有意义的变量名和函数名
- 避免过长的函数,单个函数不超过 50 行
- 复杂逻辑添加注释说明
### 安全要求
- **禁止提交敏感信息**:
- API密钥
- 密码
- Token
- 个人信息
- 签名文件 (key.jks 已在 .gitignore 中)
- **配置信息管理**:
- 服务器地址配置在 `module_base/res/values/strings.xml`
- 本地开发配置使用 `local.properties`
- 运行时配置使用 SharedPreferences
## 模块开发规范
### 新增业务页面
1. 在对应模块的 `ui/page/` 目录下创建 Activity
2. 在 `service/viewModel/` 目录下创建 ViewModel
3. 在 `bean/` 目录下定义数据模型
4. 布局文件使用 DataBinding
### 模块间通信
- 使用 ARouter 进行页面跳转
- 路由路径定义在 `module_base/router/` 目录
- 事件通信使用 FlowBus 或 EventBus
## 获取帮助
如有问题或建议,可以通过以下方式:
1. 查看项目文档:
- [CLAUDE.md](./CLAUDE.md) - 详细开发指南
- [README.md](./README.md) - 项目概览
2. 联系项目维护者
3. 提交 Issue (如果支持)
---
再次感谢你的贡献!🎉

View File

@@ -104,6 +104,53 @@
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<activity
android:name="com.lukouguoji.gjc.activity.GjcAssembleAllocateActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港出库交接 -->
<activity
android:name="com.lukouguoji.gjc.activity.IntExpOutHandoverActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港装载 -->
<activity
android:name="com.lukouguoji.gjc.activity.IntExpLoadActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港理货 -->
<activity
android:name="com.lukouguoji.gjc.activity.IntExpTallyActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港运抵 -->
<activity
android:name="com.lukouguoji.gjc.activity.IntExpArriveActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港查询 -->
<activity
android:name="com.lukouguoji.gjc.activity.GjcQueryActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港运单修改 -->
<activity
android:name="com.lukouguoji.gjc.activity.GjcQueryEditActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港查询详情 -->
<activity
android:name="com.lukouguoji.gjc.activity.GjcQueryDetailsActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<activity
android:name="com.lukouguoji.gjc.activity.GjcBoxWeighingActivity"
android:configChanges="orientation|keyboardHidden"
@@ -130,6 +177,11 @@
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<activity
android:name="com.lukouguoji.gjc.activity.GjcWeighingRecordDetailsActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<activity
android:name="com.lukouguoji.gjc.activity.GjcWeighingStartActivity"
android:configChanges="orientation|keyboardHidden"
@@ -140,6 +192,24 @@
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港-出港组装 -->
<activity
android:name="com.lukouguoji.gjc.page.assemble.IntExpAssembleActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港-开始组装 -->
<activity
android:name="com.lukouguoji.gjc.page.assemble.IntExpAssembleStartActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<!-- 国际出港-出港移库 -->
<activity
android:name="com.lukouguoji.gjc.page.move.IntExpMoveActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:screenOrientation="userLandscape" />
<activity
android:name=".MineActivity"
android:configChanges="orientation|keyboardHidden"

View File

@@ -51,6 +51,14 @@ class HomeFragment : Fragment() {
private val TAG: String = HomeFragment::class.java.simpleName
/**
* ========== 开发调试开关 ==========
* TODO: 正式发布前务必设置为 false
*/
companion object {
private const val DEV_AUTO_SELECT_INT_EXP = true // 自动选择国际出港开关
}
private var rvLeft: RecyclerView by Delegates.notNull()
private var rvRight: RecyclerView by Delegates.notNull()
@@ -70,6 +78,10 @@ class HomeFragment : Fragment() {
val leftMenuList = initLeftMenuData()
rvLeft.adapter = HomeLeftAdapt(leftMenuList)
rvRight.adapter = HomeRightAdapt(getRightMenu4Id(leftMenuList.first().id))
// ========== 开发调试:自动选择"国际出港"菜单 ==========
// TODO: 正式发布前删除此行
autoSelectIntExpForDev()
}
/////////// 左边的list循环
@@ -155,6 +167,31 @@ class HomeFragment : Fragment() {
}
override fun getItemCount() = leftMenuList.size
/**
* 开发调试:模拟点击菜单项
* TODO: 正式发布前删除此方法
*/
fun simulateClick(position: Int) {
if (position < 0 || position >= leftMenuList.size) return
val leftMenuTemp = leftMenuList[position]
// 跳过特殊菜单(航班查询、货物查询)
if (Constant.AuthName.Flight == leftMenuTemp.id ||
Constant.AuthName.CargoStatus == leftMenuTemp.id) {
return
}
// 更新选中位置
mPosition = position
// 刷新右侧菜单
refreshRight(leftMenuList[mPosition].id)
// 更新菜单状态
notifyDataSetChanged()
}
}
inner class LeftMenu(val id: String, val img: Int, val text: String)
@@ -335,7 +372,7 @@ class HomeFragment : Fragment() {
}
//移库
Constant.AuthName.GjcYiKuListActivity -> {
ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_GJC_YI_KU)
ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_INT_EXP_MOVE)
.navigation()
}
// 板箱
@@ -353,6 +390,40 @@ class HomeFragment : Fragment() {
ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_GJC_INSPECTION)
.navigation()
}
// 出港组装
Constant.AuthName.GjcIntExpAssembleActivity -> {
ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_INT_EXP_ASSEMBLE)
.navigation()
}
// 组装分配
Constant.AuthName.GjcAssembleAllocateActivity -> {
ARouter.getInstance()
.build(ARouterConstants.ACTIVITY_URL_GJC_ASSEMBLE_ALLOCATE)
.navigation()
}
// 出库交接
Constant.AuthName.GjcIntExpOutHandover -> {
ARouter.getInstance()
.build(ARouterConstants.ACTIVITY_URL_INT_EXP_OUT_HANDOVER)
.navigation()
}
// 出港装载
Constant.AuthName.GjcIntExpLoad -> {
ARouter.getInstance()
.build(ARouterConstants.ACTIVITY_URL_INT_EXP_LOAD)
.navigation()
}
// 出港理货
Constant.AuthName.GjcIntExpTally -> {
ARouter.getInstance()
.build(ARouterConstants.ACTIVITY_URL_INT_EXP_TALLY)
.navigation()
}
// 出港运抵
Constant.AuthName.GjcIntExpArrive -> {
ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE)
.navigation()
}
/**
* 国际进港
*/
@@ -595,20 +666,22 @@ class HomeFragment : Fragment() {
}
Constant.AuthName.IntExp -> {
// list.add(
// RightMenu(
// Constant.AuthName.GjcAppDomExpCheckin,
// R.mipmap.gjc_shou_yun_icon,
// "出港收运"
// )
// )
list.add(
RightMenu(
Constant.AuthName.GjcAppDomExpCheckin,
R.mipmap.gjc_shou_yun_icon,
"出港收运"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcFuBangActivity,
R.mipmap.gjc_fu_bang_icon,
"板箱过磅"
Constant.AuthName.GjcInspectionActivity,
R.mipmap.gnc_cha,
"收运检查"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcCheckWeighing,
@@ -616,20 +689,23 @@ class HomeFragment : Fragment() {
"出港计重"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcWareHouseActivity,
R.mipmap.gjc_cang_ku_icon,
"仓库管理"
Constant.AuthName.GjcFuBangActivity,
R.mipmap.gjc_fu_bang_icon,
"板箱过磅"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcYiKuListActivity,
R.mipmap.gjc_yi_ku_icon,
"出港移库"
Constant.AuthName.GjcIntExpAssembleActivity,
com.lukouguoji.module_base.R.drawable.img_gjc_banxiangzuzhuang,
"出港组装"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcQueryListActivity,
@@ -640,25 +716,76 @@ class HomeFragment : Fragment() {
list.add(
RightMenu(
Constant.AuthName.GjcBanXListActivity,
Constant.AuthName.GjcYiKuListActivity,
R.mipmap.gjc_yi_ku_icon,
"出港移库"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcAssembleAllocateActivity,
com.lukouguoji.module_base.R.drawable.img_gjc_banxiangzuzhuang,
"板箱组装"
"组装分配"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcGoodsListActivity,
R.mipmap.img_hwjj,
"货物交接"
Constant.AuthName.GjcIntExpOutHandover,
com.lukouguoji.module_base.R.drawable.img_gjc_banxiangzuzhuang,
"出库交接"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcInspectionActivity,
R.mipmap.gnc_cha,
"收运检查"
Constant.AuthName.GjcIntExpLoad,
com.lukouguoji.module_base.R.drawable.img_gjc_banxiangzuzhuang,
"出港装载"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcIntExpTally,
com.lukouguoji.module_base.R.drawable.img_gjc_banxiangzuzhuang,
"出港理货"
)
)
list.add(
RightMenu(
Constant.AuthName.GjcIntExpArrive,
com.lukouguoji.module_base.R.drawable.img_gjc_banxiangzuzhuang,
"出港运抵"
)
)
// list.add(
// RightMenu(
// Constant.AuthName.GjcWareHouseActivity,
// R.mipmap.gjc_cang_ku_icon,
// "仓库管理"
// )
// )
// list.add(
// RightMenu(
// Constant.AuthName.GjcBanXListActivity,
// com.lukouguoji.module_base.R.drawable.img_gjc_banxiangzuzhuang,
// "板箱组装"
// )
// )
// list.add(
// RightMenu(
// Constant.AuthName.GjcGoodsListActivity,
// R.mipmap.img_hwjj,
// "货物交接"
// )
// )
}
Constant.AuthName.IntImp -> {
@@ -816,4 +943,26 @@ class HomeFragment : Fragment() {
private fun refreshRight(id: String) {
(rvRight.adapter as? HomeRightAdapt)?.refresh(getRightMenu4Id(id))
}
/**
* 开发调试:自动选择"国际出港"菜单
* TODO: 正式发布前删除此方法或将 DEV_AUTO_SELECT_INT_EXP 设置为 false
*/
private fun autoSelectIntExpForDev() {
if (!DEV_AUTO_SELECT_INT_EXP) return
// 延迟执行,确保适配器已初始化
rvLeft.postDelayed({
val leftAdapter = rvLeft.adapter as? HomeLeftAdapt ?: return@postDelayed
// 查找"国际出港"在左侧菜单的位置
val leftMenuList = initLeftMenuData()
val intExpIndex = leftMenuList.indexOfFirst { it.id == Constant.AuthName.IntExp }
if (intExpIndex >= 0) {
// 模拟点击左侧"国际出港"菜单项
leftAdapter.simulateClick(intExpIndex)
}
}, 300) // 延迟300ms确保适配器已绑定数据
}
}

View File

@@ -37,6 +37,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import me.jessyan.autosize.internal.CustomAdapt
/**
* ========== 开发调试开关 ==========
* TODO: 正式发布前务必设置为 false
*/
private const val DEV_AUTO_LOGIN = true // 自动登录开关
@Route(path = ARouterConstants.ACTIVITY_URL_LOGIN)
class LoginActivity : BaseActivity(),
@@ -100,6 +105,15 @@ class LoginActivity : BaseActivity(),
spinner.setSelection(index)
}
}
// ========== 开发调试:角色信息获取成功后自动登录 ==========
// TODO: 正式发布前删除此代码块
if (DEV_AUTO_LOGIN && user.text.toString() == "ADMIN") {
loginButton.postDelayed({
val encodedPassword = "$2a$10$02ZpVb/bymrybmPE2Mu2C.O.JcMXTB..gkssaNn8q2EC.kUAfJP0S"
viewModel.login(user.text.toString(), encodedPassword)
}, 200) // 短暂延迟确保spinner更新完成
}
}
bindOnSelected(spinner, object : IOnSpinnerSelected {
override fun onSelected(position: Int) {
@@ -183,6 +197,28 @@ class LoginActivity : BaseActivity(),
bindAdapter(spinner, viewModel.roleList, "请选择角色", R.layout.item_spinner_list_18sp)
setEnable(spinner, false)
// ========== 开发调试:自动登录 ==========
// TODO: 正式发布前删除此行
autoLoginForDev()
}
/**
* 开发调试:自动登录
* TODO: 正式发布前删除此方法或将 DEV_AUTO_LOGIN 设置为 false
*/
private fun autoLoginForDev() {
if (!DEV_AUTO_LOGIN) return
// 延迟执行确保UI初始化完成
loginButton.postDelayed({
// 设置用户名
user.setText("ADMIN")
// 手动触发获取角色信息
// 角色信息获取成功后会在userRoleBean.observe中自动执行登录
viewModel.getUserRole("ADMIN")
}, 500) // 延迟500ms确保ViewModel已初始化
}

View File

@@ -0,0 +1,19 @@
package com.lukouguoji.module_base.bean
import dev.utils.app.info.KeyValue
/**
* 组装公司Bean
* 用于组装分配时选择分配人
*/
data class AssembleCompanyBean(
val code: String = "", // 公司代码,例如 "ATR"
val name: String = "" // 公司名称,例如 "ATR:马道"
) {
/**
* 转换为 KeyValue 用于下拉列表
* @return KeyValue(显示文本, 实际值)
* 例如: KeyValue("ATR:马道", "ATR")
*/
fun toKeyValue() = KeyValue(name, code)
}

View File

@@ -0,0 +1,53 @@
package com.lukouguoji.module_base.bean
import androidx.databinding.ObservableBoolean
/**
* 组装信息Bean(左侧折叠列表)
* 支持两种行类型:
* 1. 一级ULD行: 显示ULD编号、总件数、总重量
* 2. 二级运单行: 显示运单号、件数、重量
*/
class AssembleInfoBean {
// ========== 层级类型 ==========
enum class ItemType {
ULD_HEADER, // 一级ULD行
WAYBILL_DETAIL // 二级运单行
}
var itemType: ItemType = ItemType.ULD_HEADER // 行类型
// ========== 一级ULD行字段 ==========
var uldNo: String = "" // ULD编号
var uldIndex: Int = 0 // ULD序号用于显示1、2、3...
var totalPieces: Int = 0 // 总件数(二级运单件数求和)
var totalWeight: Double = 0.0 // 总重量(二级运单重量求和)
// 展开/折叠状态使用ObservableBoolean支持DataBinding
val isExpanded: ObservableBoolean = ObservableBoolean(false)
// 子运单列表(用于数据管理,不直接显示)
var waybillChildren: MutableList<AssembleInfoBean> = mutableListOf()
// ========== 二级运单行字段 ==========
var parentUldNo: String = "" // 父级ULD编号用于关联
var wbNo: String = "" // 运单号(直接使用后端字段名)
var waybillPieces: Int = 0 // 运单件数
var waybillWeight: Double = 0.0 // 运单重量
// 原始运单数据(用于同步更新和填充表单)
var waybillData: AssembleWaybillBean? = null
// 关联的原始数据用于存储GjcUldUseBean等对象
var tag: Any? = null
// ========== 视觉样式字段 ==========
var hasArrow: Boolean = false // 是否显示箭头一级ULD行为true
var isOrange: Boolean = false // 是否橙色文字(暂保留)
var showIndex: Boolean = false // 是否显示序号圆圈改为false不再显示序号
var showIndent: Boolean = false // 是否显示缩进二级运单行为true
// 保留原有的weightInfo字段兼容性但不再使用
var weightInfo: String = ""
}

View File

@@ -0,0 +1,9 @@
package com.lukouguoji.module_base.bean
/**
* 组装位置Bean
*/
class AssemblePositionBean {
var positionName: String = "" // 位置名称
var isSelected: Boolean = false // 是否选中
}

View File

@@ -0,0 +1,28 @@
package com.lukouguoji.module_base.bean
import androidx.databinding.ObservableBoolean
/**
* 运单列表Bean右侧运单列表
*/
class AssembleWaybillBean {
var waybillNo: String = "" // 运单号
var pieces: String = "" // 件数(累积的组装件数)
var weight: String = "" // 重量(累积的组装重量)
var flight: String = "" // 配载航班
var isMarked: Boolean = false // 是否标记(红色显示)
var fno: String = ""
var fdate: String = ""
var whId: Long = 0 // 运单ID用于接口调用
// ========== 原始运单信息(用于编辑模式回显) ==========
var originalPieces: String = "" // 原始运单件数
var originalWeight: String = "" // 原始运单重量
val fLightInfo: String
get() = "$fno/${fdate.replace("-", "")}"
// ========== UI扩展字段 ==========
val isSelected: ObservableBoolean = ObservableBoolean(false) // 选中状态
}

View File

@@ -0,0 +1,17 @@
package com.lukouguoji.module_base.bean
import dev.utils.app.info.KeyValue
/**
* 字典-Location接口返回数据Bean
*/
data class DictLocationBean(
var code: String = "", // 编码值 (例如: "18")
var name: String = "" // 显示名称 (例如: "国际出港通道01")
) {
/**
* 转换为KeyValue
* KeyValue(显示名称, 编码值)
*/
fun toKeyValue() = KeyValue(name, code)
}

View File

@@ -0,0 +1,30 @@
package com.lukouguoji.module_base.bean
import androidx.databinding.ObservableBoolean
import com.lukouguoji.module_base.interfaces.ICheck
/**
* 国际出港组装分配Bean
*/
data class GjcAssembleAllocate(
val abDate: String? = null, // 组装时间
val abName: String? = null, // 组装公司(中文)
val abid: String? = null, // 组装公司ID
val acDate: String? = null, // 分配时间
val acName: String? = null, // 分配人(中文)
val acid: String? = null, // 分配人ID
val fdate: String? = null, // 航班日期
val fdep: String? = null, // 始发站
val fdest: String? = null, // 目的站
val fid: Long? = null, // 航班id
val fidList: List<Long>? = null, // 航班fid列表
val fno: String? = null, // 航班号
val id: Long? = null, // 主键
val jtz: String? = null, // 经停站
val range: String? = null, // 航程
// 用于UI的可观察选中状态
val checked: ObservableBoolean = ObservableBoolean(false)
) : ICheck {
override fun getCheckObservable(): ObservableBoolean = checked
}

View File

@@ -0,0 +1,10 @@
package com.lukouguoji.module_base.bean
/**
* 国际出港组装记录Bean
* 对应API: /IntExpAssemble/queryAssembled
*/
class GjcAssembled {
var uldUse: GjcUldUseBean? = null // ULD信息
var warehouseList: MutableList<GjcWarehouse>? = null // 运单列表
}

View File

@@ -0,0 +1,14 @@
package com.lukouguoji.module_base.bean
/**
* 国际出港-分页查询请求参数
* 用于出港装载、检入记录等列表查询
*/
data class GjcCheckInPage(
var fdate: String? = null, // 航班日期
var fno: String? = null, // 航班号
var no: String? = null, // 运单号
var hno: String? = null, // 分单号
var pageNum: Int = 1, // 页码
var pageSize: Int = 10 // 每页条数
)

View File

@@ -0,0 +1,55 @@
package com.lukouguoji.module_base.bean
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import androidx.databinding.library.baseAdapters.BR
/**
* 国际出港计重记录明细Bean
* 用于:加载运单的所有计重记录 + 批量更新
* 对应API:
* - POST /IntExpCheckIn/listRecordByWh (加载)
* - POST /IntExpCheckIn/updateRecordList (保存)
*/
data class GjcCheckInRecord(
var id: Long = 0, // 收运记录id
var maWbId: Long = 0, // GJC_MAWB.MAWBID
var no: String = "", // 运单号
var prefix: String = "", // 运单前缀
var opDate: String = "", // 操作时间
var opId: String = "", // 操作员id
var pc: Long = 0, // 运抵件数
var weight: Double = 0.0, // 运抵重量
var volume: Double = 0.0, // 运抵体积
var whId: Long = 0 // GJC_WAREHOUSE.ID
) : BaseObservable() {
// 数据变化回调
var onDataChanged: (() -> Unit)? = null
// 件数的字符串表示(用于双向绑定)
@get:Bindable
var pcStr: String
get() = if (pc == 0L) "" else pc.toString()
set(value) {
val newPc = value.toLongOrNull() ?: 0L
if (pc != newPc) {
pc = newPc
notifyPropertyChanged(BR.pcStr)
onDataChanged?.invoke()
}
}
// 重量的字符串表示(用于双向绑定)
@get:Bindable
var weightStr: String
get() = if (weight == 0.0) "" else weight.toString()
set(value) {
val newWeight = value.toDoubleOrNull() ?: 0.0
if (weight != newWeight) {
weight = newWeight
notifyPropertyChanged(BR.weightStr)
onDataChanged?.invoke()
}
}
}

View File

@@ -0,0 +1,13 @@
package com.lukouguoji.module_base.bean
/**
* 国际出港-装载申报/状态重置/删除申报 请求参数
*/
data class GjcDeclareParam(
var dcode: String? = null, // 变更原因代码
var dcontactsName: String? = null, // 联系人姓名
var dcontactsTel: String? = null, // 联系人电话
var maWbList: List<GjcMaWb>? = null, // 主单列表
var haWbList: List<GjcHaWb>? = null, // 分单列表
var loadList: List<GjcExportLoad>? = null // 装载记录列表
)

View File

@@ -0,0 +1,63 @@
package com.lukouguoji.module_base.bean
import androidx.databinding.ObservableBoolean
/**
* 国际出港-出港装载Bean
* 对应API: IntExpLoad/pageQuery
*/
class GjcExportLoad {
var activeId: String = "" // 运单活跃号
var by1: String = "" // 第一承运人
var dep: String = "" // 始发港
var fdate: String = "" // 申报航班日期
var fid: String = "" // 航班主键id
var fno: String = "" // 申报航班号
var goods: String = "" // 品名
var lastLoadMsgId: String = "" // 上一次装载申报编号
var lastLoadStatus: String = "" // 上一次装载状态
var loadMsgId: String = "" // 报文申报编号
var loadStatus: String = "" // 装载申报状态("01"等状态码)
var loaddCount: Int = 0 // 装载删除计次
var loaddRate: Double = 0.0 // 装载删除费率
var loadsCount: Int = 0 // 装载申报计次
var loadsRate: Double = 0.0 // 装载申报费率
var no: String = "" // 主运单号
var packageType: String = "" // 包装类型代码
var pc: Int = 0 // 件数
var prefix: String = "" // 主运单前缀
var response: String = "" // 海关业务回执
var spCode: String = "" // 特码
var splitFlag: String = "" // 分批标志
var sysId: String = "" // 系统ID
var tallyStatus: String = "" // 理货状态
var weight: Double = 0.0 // 重量
// ========== UI扩展字段 ==========
val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态
// 兼容现有API的isSelected属性
var isSelected: Boolean
get() = checked.get()
set(value) = checked.set(value)
/**
* 获取格式化的运单号prefix + no
*/
fun getFullWaybillNo(): String {
return if (prefix.isNotEmpty()) "$prefix$no" else no
}
/**
* 获取装载状态显示文字
*/
fun getLoadStatusText(): String {
return when (loadStatus) {
"01" -> "已申报"
"02" -> "申报中"
"03" -> "申报失败"
"04" -> "已删除"
else -> loadStatus
}
}
}

View File

@@ -0,0 +1,41 @@
package com.lukouguoji.module_base.bean
/**
* 国际出港货物交接单Bean
*/
data class GjcHandoverSheetBean(
// ========== API文档字段 ==========
var id: Long? = null, // 主键id
var maWbId: Long? = null, // 运单主键id
var no: String? = null, // 编号
var prefix: String? = null, // 前缀
var chargeWeight: Double? = null, // 计费重量
var saleAgentCode: String? = null, // 销售代理人
var expressName: String? = null, // 平台或快递企业名称
var cbEcFlag: String? = null, // 是否为跨境电商货物(0:否;1:是)
var opName: String? = null, // 经办人签名
var opCardId: String? = null, // 经办人身份证号
// ========== 防止隐含危险品检查单 ==========
var goodsName: String? = null, // 申报货物的品名为确指品名(0/1)
var danger: String? = null, // 确认货物内不含未申报的危险品(0/1)
var fish: String? = null, // 是否非观赏鱼类货物(0/1)
var packaging: String? = null, // 包装件没有油渍或液体渗漏(0/1)
var labels: String? = null, // 清除或涂去无关标记或标签(0/1)
// ========== 高风险货物检查单 ==========
var appearance: String? = null, // 航空货物外观显现异常(0/1)
var threat: String? = null, // 有具体情报显示威胁(0/1)
var highRisk: String? = null, // 结论:是否可以判定为高风险货物(0/1)
// ========== 跨境电商检查单 ==========
var submitStatement: String? = null, // 是否已提交声明(0/1/2: 是/否/不适用)
// ========== 货站收运人员 ==========
var staOpName: String? = null, // 货站收运人员签名
// ========== 安检信息(用户手动填写) ==========
var securityChannel: String? = null, // 安检机通道号
var securityStartTime: String? = null, // 开始过机时间
var securityEndTime: String? = null // 结束过机时间
)

View File

@@ -0,0 +1,9 @@
package com.lukouguoji.module_base.bean
/**
* 国际出港货物交接单查询接口返回Bean
*/
data class GjcHandoverSheetResponse(
var maWb: GjcMaWb? = null, // 主单信息
var handoverSheet: GjcHandoverSheetBean? = null // 交接单信息(可能为null)
)

View File

@@ -2,6 +2,9 @@ package com.lukouguoji.module_base.bean
import androidx.databinding.ObservableBoolean
import com.lukouguoji.module_base.interfaces.ICheck
import com.lukouguoji.module_base.ktx.noNull
import dev.utils.DevFinal
import dev.utils.common.DateUtils
/**
* 国际出港收运检查数据Bean
@@ -74,4 +77,32 @@ class GjcInspectionBean : ICheck {
else -> "#9E9E9E" // 灰色-未审核
}
}
/**
* 预计起飞时间 - 仅时分格式 (HH:mm)
*/
val scheduledTackOffHM: String
get() {
return DateUtils.parseString(
scheduledTackOff,
DevFinal.TIME.yyyyMMddHHmmss_HYPHEN,
"HH:mm"
).noNull(scheduledTackOff)
}
/**
* 验证预计起飞时间是否为次日
*/
fun verifyScheduledTackOffNextDay(): Boolean {
if (fdate.isEmpty() || scheduledTackOff.isEmpty()) {
return false
}
return try {
val calendarFDate = DateUtils.getCalendar(fdate, DevFinal.TIME.yyyyMMdd_HYPHEN)
val calendarTakeOff = DateUtils.getCalendar(scheduledTackOff, DevFinal.TIME.yyyyMMddHHmmss_HYPHEN)
DateUtils.getDay(calendarTakeOff) > DateUtils.getDay(calendarFDate)
} catch (e: Exception) {
false
}
}
}

View File

@@ -0,0 +1,245 @@
package com.lukouguoji.module_base.bean
import androidx.databinding.ObservableBoolean
import java.util.Date
/**
* 国际出港主单数据模型
* 对应接口:/IntExpSearch/pageQuery 返回的列表项
*/
data class GjcMaWb(
// ==================== 主键与基础信息 ====================
var maWbId: Long? = null, // 主单主键ID
var wbNo: String? = null, // 11位运单号
var prefix: String? = null, // 运单号前缀3位
var no: String? = null, // 运单号主体8位
var oldPrefix: String? = null, // 旧运单前缀
var oldNo: String? = null, // 旧运单号
// ==================== 航班信息 ====================
var fid: Long? = null, // 航班主键id
var fno: String? = null, // 航班号
var fdate: Date? = null, // 航班日期
var flight: String? = null, // 航班: 航班日期/航班号
var fclose: String? = null, // 航班关闭时间
var scheduledTackOff: Date? = null, // 计划起飞时间
var scheduledArrival: Date? = null, // 预计到达时间
// ==================== 目的地信息 ====================
var dep: String? = null, // 始发站
var dest: String? = null, // 最终目的站
var dest1: String? = null, // 第一目的站
var dest2: String? = null, // 第二目的站
var range: String? = null, // 航程
// ==================== 代理与承运人 ====================
var agentCode: String? = null, // 代理人code
var agentName: String? = null, // 代理人名称
var customsLib: String? = null, // 代理人lib
var by0: String? = null, // 第一承运人
var by1: String? = null, // 第二承运人
var by2: String? = null, // 第三承运人
// ==================== 货物信息 ====================
var pc: Long? = null, // 预配件数
var weight: Double? = null, // 预配重量KG
var volume: Double? = null, // 预配体积CBM
var goods: String? = null, // 品名(英)
var goodsCn: String? = null, // 品名(中)
var packageType: String? = null, // 包装类型
var cargoType: String? = null, // 货物类型
var origin: String? = null, // 货源地
// ==================== 运抵信息 ====================
var arrivePc: Long? = null, // 运抵件数
var arriveWeight: Double? = null, // 运抵重量
var arriveVolume: Double? = null, // 运抵体积
var arriveFlag: String? = null, // 运抵状态0正常运抵1提前运抵
// ==================== 特码与收货人 ====================
var spCode: String? = null, // 特码
var subCode: String? = null, // 子码
var consignee: String? = null, // 收货人
var cneeTel: String? = null, // 收货人电话
// ==================== 运单类型 ====================
var awbType: String? = null, // 运单类型code
var awbName: String? = null, // 运单类型名称(中)
var businessType: String? = null, // 业务类型code
var businessName: String? = null, // 业务类型名称(中)
// ==================== 车辆信息 ====================
var carId: String? = null, // 平板车号
var carNumber: String? = null, // 车牌号
var passageWay: String? = null, // 通道号
// ==================== 状态信息 ====================
var checkIn: String? = null, // 收运状态。0待收运1已收运2收运中
var declareStatus: String? = null, // 申报状态
var reviewStatus: String? = null, // 审核状态0未审核1通过2退回
var tranFlag: String? = null, // 转运标志
// ==================== 操作信息 ====================
var opDate: String? = null, // 操作时间(入库时间)
var opId: String? = null, // 操作员id
var paperTime: Date? = null, // 单证时间
// ==================== 备注与其他 ====================
var remark: String? = null, // 备注
var ffmMemo: String? = null, // ffm备注
var mftMemo: String? = null, // mft备注
var unNumber: String? = null, // UN编号
var billsNo: String? = null, // 提单号
var recheckCount: Long? = null, // 复核次数
// ==================== 地区信息 ====================
var proName: String? = null, // 省份名称
var cityName: String? = null, // 城市名称
var areaName: String? = null, // 区域名称
// ==================== 关联数据统计 ====================
var haWbNumber: Int? = null, // 分单数
var storageUseNumber: Int? = null, // 库位使用数
// ==================== 活动标识 ====================
var activeId: Long? = null, // 有效值
// ==================== 关联列表(非数据库字段,用于展示) ====================
var haWbList: List<GjcHaWb>? = null, // 分单列表
var storageUseList: List<GjcStorageUse>? = null, // 库位使用列表
var attachList: List<ComAttach>? = null // 附件列表
) {
// ==================== UI扩展字段 ====================
val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态
// 兼容现有API的isSelected属性
var isSelected: Boolean
get() = checked.get()
set(value) = checked.set(value)
// ==================== 状态转换扩展属性 ====================
/**
* 计重状态中文
* 0-待收运1-已计重2-计重中
*/
val checkInText: String
get() = when (checkIn) {
"0" -> "待收运"
"1" -> "已计重"
"2" -> "计重中"
else -> checkIn ?: ""
}
/**
* 运抵状态中文
* 0-正常运抵1-提前运抵
*/
val arriveFlagText: String
get() = when (arriveFlag) {
"0" -> "正常运抵"
"1" -> "提前运抵"
else -> arriveFlag ?: ""
}
}
/**
* 国际出港分单数据模型
*/
data class GjcHaWb(
var haWbId: Long? = null, // 主键ID
var fid: Long? = null, // 航班主键
var prefix: String? = null, // 前缀
var no: String? = null, // 编号
var hno: String? = null, // H编号
var pc: Long? = null, // PC数量
var weight: Double? = null, // 重量
var volume: Double? = null, // 体积
var hpc: Long? = null, // 分单数量
var hweight: Double? = null, // 分单重量
var hvolume: Double? = null, // 分单体积
var goods: String? = null, // 货物英文
var goodsCn: String? = null, // 货物中文
var billsNo: String? = null, // 提单号
var opDate: Date? = null, // 操作日期
var opId: String? = null, // 操作人ID
var remark: String? = null, // 备注
// 发货人信息
var consignorName: String? = null, // 发货人名称
var consignorCode: String? = null, // 发货人代码
var consignorAddress: String? = null, // 发货人地址
var consignorCountryCode: String? = null, // 发货人国家代码
var consignorComType: String? = null, // 发货人公司类型
var consignorPnum: String? = null, // 发货人电话号码
// 收货人信息
var consigneeName: String? = null, // 收货人名称
var consigneeCode: String? = null, // 收货人代码
var consigneeAddress: String? = null, // 收货人地址
var consigneeCountryCode: String? = null, // 收货人国家代码
var consigneeComType: String? = null, // 收货人公司类型
var consigneePnum: String? = null, // 收货人电话号码
// 特殊收货人信息
var speConsigneeName: String? = null, // 特殊收货人名称
var speConsigneeComType: String? = null, // 特殊收货人公司类型
var speConsigneePnum: String? = null, // 特殊收货人电话号码
// 申报状态
var mftStatus: String? = null, // 预配状态
var mftMsgId: String? = null, // 预配消息ID
var arrivalStatus: String? = null, // 运抵状态
var arrMsgId: String? = null, // 运抵消息ID
var tallyStatus: String? = null, // 理货状态
var tallyMsgId: String? = null, // 理货消息ID
var transferStatus: String? = null, // 传输状态
var tranMsgId: String? = null, // 传输消息ID
var cmdStatus: String? = null, // 命令状态
var response: String? = null, // 响应
// 最后状态
var lastMftStatus: String? = null, // 最后预配状态
var lastMftMsgId: String? = null, // 最后预配消息ID
var lastArrStatus: String? = null, // 最后运抵状态
var lastArrMsgId: String? = null, // 最后运抵消息ID
var lastTallyStatus: String? = null, // 最后理货状态
var lastTallyMsgId: String? = null, // 最后理货消息ID
// 费率计次
var mftSRate: Double? = null, // 预配申报费率
var mftSCount: Double? = null, // 预配申报计次
var mftDRate: Double? = null, // 预配删除费率
var mftDCount: Double? = null, // 预配删除计次
var arrivalSRate: Double? = null, // 运抵申报费率
var arrivalSCount: Double? = null, // 运抵申报计次
var arrivalDRate: Double? = null, // 运抵删除费率
var arrivalDCount: Double? = null, // 运抵删除计次
var tallySRate: Double? = null, // 出港理货申报费率
var tallySCount: Double? = null, // 出港理货申报计次
var tallyDRate: Double? = null, // 出港理货删除费率
var tallyDCount: Double? = null, // 出港理货删除计次
// 操作信息
var arrivalOpDate: Date? = null, // 运抵操作日期
var arrivalOpId: String? = null, // 运抵操作人ID
var tallyOpDate: Date? = null, // 理货操作日期
var tallyOpId: String? = null, // 理货操作人ID
var declareCount: Long? = null, // 申报次数
var activeId: Long? = null // 活动ID
) {
// ==================== UI扩展字段 ====================
val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态
// 兼容现有API的isSelected属性
var isSelected: Boolean
get() = checked.get()
set(value) = checked.set(value)
}
/**
* 库位使用记录
*/
data class GjcStorageUse(
var id: Long? = null, // 主键
var maWbId: Long? = null, // 运单id
var prefix: String? = null, // 运单前缀
var no: String? = null, // 运单号
var storageCode: String? = null, // 库位号
var inDate: Date? = null, // 入库时间
var inId: String? = null, // 入库人
var outDate: Date? = null, // 出库时间
var outId: String? = null // 出库人
)

View File

@@ -0,0 +1,47 @@
package com.lukouguoji.module_base.bean
import java.util.Date
/**
* 国际出港查询参数封装类
* 对应接口:/IntExpSearch/pageQuery
*/
data class GjcMostMouldPage(
// ==================== 分页参数 ====================
var pageNum: Int? = null, // 页号
var pageSize: Int? = null, // 每页显示条数
// ==================== 基础搜索条件 ====================
var fdate: Date? = null, // 航班日期
var fno: String? = null, // 航班号
var agentCode: String? = null, // 代理人code
var outState: Int? = null, // 出库状态0未出库1:已出库)
var wbNo: String? = null, // 运单号11位
var no: String? = null, // 运单号
// ==================== 筛选条件 ====================
var spCode: String? = null, // 特码
var dest: String? = null, // 目的港
var awbType: String? = null, // 运单类型
var businessType: String? = null, // 业务类型
var goodsCn: String? = null, // 品名(中)
// ==================== 其他可选条件 ====================
var beginDate: Date? = null, // 航班开始时间
var endDate: Date? = null, // 航班结束时间
var by1: String? = null, // 承运人
var carId: String? = null, // 板车号
var dest1: String? = null, // 卸货站
var fdest: String? = null, // 目的站
var handoverState: Int? = null, // 交接状态0未交接1:已交接)
var isFclose: Boolean? = null, // 筛选航班是否关闭
var ldId: String? = null, // 组装人
var likeNo: String? = null, // 运单号(模糊查询)
var moveState: Int? = null, // 移库状态
var prefix: String? = null, // 运单号前缀
var uld: String? = null, // uld编号
// 分页辅助字段
var startIndex: Int? = null,
var endIndex: Int? = null
)

View File

@@ -0,0 +1,57 @@
package com.lukouguoji.module_base.bean
import androidx.databinding.ObservableBoolean
import com.lukouguoji.module_base.interfaces.ICheck
import java.io.Serializable
/**
* 国际出港移库Bean
*/
data class GjcMove(
var no: String = "", // ID
var prefix: String = "", // 运单前缀
var wbNo: String = "", // 运单号
var pc: Long = 0, // 件数
var weight: Double = 0.0, // 重量
var volume: Double = 0.0, // 体积
var dep: String = "", // 起运港
var dest: String = "", // 目的港
var dest1: String = "", // 卸货站1
var dest2: String = "", // 卸货站2
var by1: String = "", // 承运人1
var by2: String = "", // 承运人2
var awbType: String = "", // 运单类型编码
var awbTypeName: String = "", // 运单类型名称
var businessType: String = "", // 业务类型
var moveState: Int = 0, // 移库状态0-未移交1-已移交)
var goods: String = "", // 品名(英文)
var goodsCn: String = "", // 品名(中文)
var agentName: String = "", // 代理人
var agentCode: String = "", // 代理代码
var spCode: String = "", // SP代码
var subCode: String = "", // 子代码
var packageType: String = "", // 包装类型
var cargoType: String = "", // 货物类型
var origin: String = "", // 始发地
var maWbId: Long = 0, // GJC_MAWB.MAWBID
var moveId: String = "", // 移动ID
var opId: String = "", // 操作人ID
var opdate: String = "", // 操作日期
var remark: String = "", // 备注
var likeNo: String = "", // 部分运单号no模糊查询
// UI扩展字段 - 使用ObservableBoolean实现自动UI更新
val checked: ObservableBoolean = ObservableBoolean(false)
) : Serializable, ICheck {
// 实现ICheck接口
override fun getCheckObservable(): ObservableBoolean = checked
// 兼容现有代码的属性如果其他地方使用isSelected
var isSelected: Boolean
get() = checked.get()
set(value) = checked.set(value)
}

View File

@@ -1,5 +1,7 @@
package com.lukouguoji.module_base.bean
import androidx.databinding.ObservableBoolean
/**
* 国际出港板箱过磅-ULD使用记录Bean
* 对应API: IntExpWeighting/pageQuery
@@ -14,6 +16,7 @@ class GjcUldUseBean {
var netWeight: Double = 0.0 // 装机重量
var totalWeight: Double = 0.0 // 总重
var uldWeight: Double = 0.0 // uld重量
var consumeWeight: Double = 0.0 // 耗材重量
var volume: Double = 0.0 // 体积
var maxVolume: Double = 0.0 // uld最大容积
var maxWeight: Double = 0.0 // uld最大载重
@@ -24,6 +27,10 @@ class GjcUldUseBean {
var fdest: String = "" // 目的港
var fClose: String = "" // 航班关闭时间
// 格式化后的航班日期(只保留年月日)
val fdateFormatted: String
get() = if (fdate.contains(" ")) fdate.split(" ")[0] else fdate
var wtId: String = "" // 过磅人ID
var wtUsername: String = "" // 过磅人
var wtDate: String = "" // 过磅时间
@@ -51,4 +58,14 @@ class GjcUldUseBean {
var remark: String = "" // 备注
var checkFlag: String = "" // 检查标记
var emptyUld: String = "" // 空ULD
// ========== 出港组装页面扩展字段 ==========
var isExpanded: Boolean = false // 展开状态
val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态(Observable)
var waybillDetails: MutableList<GjcWarehouse>? = null // 运单明细缓存
// 兼容原有代码的isSelected属性
var isSelected: Boolean
get() = checked.get()
set(value) = checked.set(value)
}

View File

@@ -0,0 +1,37 @@
package com.lukouguoji.module_base.bean
/**
* 国际出港-运单明细Bean
* 对应API: IntExpAssemble/queryAssembled
*/
class GjcWarehouse {
var whId: Long = 0 // ID
var no: String = "" // 运单号11位
var prefix: String = "" // 运单前缀
var wbNo: String = "" // 主运单编号
var pc: Long = 0 // 件数
var weight: Double = 0.0 // 重量
var volume: Double = 0.0 // 体积
var agentCode: String = "" // 代理code
var agentName: String = "" // 代理名称
var dest: String = "" // 目的港
var dest1: String = "" // 卸货站
var dest2: String = "" // 第二目的地
var dep: String = "" // 始发站
var spCode: String = "" // 特码
var goods: String = "" // 品名(英)
var goodsCn: String = "" // 品名(中)
var fdate: String = "" // 航班日期
var fno: String = "" // 航班号
var fclose: String = "" // 航班关闭时间
var flight: String = "" // 航班:航班日期/航班号
var range: String = "" // 航程
var businessType: String = "" // 业务类型
var opId: String = "" // 收运人ID
var userName: String = "" // 收运人
var opDate: String = "" // 收运时间
var location: String = "" // uld
var checkInPc: Long = 0 // 入库件数
var checkInWeight: Double = 0.0 // 入库重量
var assembleCount: Int = 0 // 已经组装的数量
}

View File

@@ -1,5 +1,9 @@
package com.lukouguoji.module_base.bean
import com.lukouguoji.module_base.ktx.noNull
import dev.utils.DevFinal
import dev.utils.common.DateUtils
/**
* 国际出港待计重-列表数据Bean
* 对应API: IntExpCheckIn/pageQuery
@@ -91,4 +95,32 @@ class GjcWeighingBean {
var haWbList: List<Any>? = null // 分单列表
var storageUseList: List<Any>? = null // 库位使用列表
var attachList: List<Any>? = null // 附件列表
/**
* 预计起飞时间 - 仅时分格式 (HH:mm)
*/
val scheduledTackOffHM: String
get() {
return DateUtils.parseString(
scheduledTackOff,
DevFinal.TIME.yyyyMMddHHmmss_HYPHEN,
"HH:mm"
).noNull(scheduledTackOff)
}
/**
* 验证预计起飞时间是否为次日
*/
fun verifyScheduledTackOffNextDay(): Boolean {
if (fdate.isEmpty() || scheduledTackOff.isEmpty()) {
return false
}
return try {
val calendarFDate = DateUtils.getCalendar(fdate, DevFinal.TIME.yyyyMMdd_HYPHEN)
val calendarTakeOff = DateUtils.getCalendar(scheduledTackOff, DevFinal.TIME.yyyyMMddHHmmss_HYPHEN)
DateUtils.getDay(calendarTakeOff) > DateUtils.getDay(calendarFDate)
} catch (e: Exception) {
false
}
}
}

View File

@@ -1,11 +1,13 @@
package com.lukouguoji.module_base.bean
import java.io.Serializable
/**
* 国际出港计重记录-列表数据Bean
* 对应API: IntExpCheckIn/checked/pageQuery
* 对应API返回类型: GjcWarehouse
*/
class GjcWeighingRecordBean {
class GjcWeighingRecordBean : Serializable {
var whId: Long = 0 // 仓库主键ID
var no: String = "" // 运单号
var prefix: String = "" // 运单前缀

View File

@@ -0,0 +1,48 @@
package com.lukouguoji.module_base.bean
import java.util.ArrayList
/**
* 分页响应容器 (适配MyBatis-Plus的PageInfo结构)
* 用于适配接口返回的PageInfo<T>结构
*/
class PageInfo<T> {
// 当前页号
var pageNum: Int = 1
// 每页显示条数
var pageSize: Int = 10
// 数据总量
var total: Long = 0
// 页面总量
var pages: Int = 1
// 数据列表
var list: ArrayList<T>? = null
// 是否有下一页
var hasNextPage: Boolean = false
// 是否有上一页
var hasPreviousPage: Boolean = false
// 是否是第一页
var isFirstPage: Boolean = true
// 是否是最后一页
var isLastPage: Boolean = false
/**
* 转换为项目使用的BaseListBean
* 这是核心方法用于适配项目中的PageModel
*/
fun toBaseListBean(): BaseListBean<T> {
return BaseListBean<T>().apply {
this.pages = this@PageInfo.pages
this.total = this@PageInfo.total.toInt()
this.list = this@PageInfo.list
}
}
}

View File

@@ -0,0 +1,15 @@
package com.lukouguoji.module_base.bean
/**
* ULD信息Bean
*/
class UldInfoBean {
var uldNo: String = "" // ULD编号
var materialWeight: String = "" // 耗材重量
var uldStatus: String = "" // ULD状态
var planeType: String = "" // 机型
var weightLimit: String = "" // 限重
var totalPieces: String = "" // 总件数
var totalWeight: String = "" // 总重量
var status: String = "" // 状态(旧字段,保留兼容)
}

View File

@@ -0,0 +1,18 @@
package com.lukouguoji.module_base.bean
/**
* 运单信息Bean
*/
class WaybillInfoBean {
var waybillNo: String = "" // 运单号
var waybillPieces: String = "" // 运单件数
var waybillWeight: String = "" // 运单重量
var assembleCount: String = "" // 组装件数
var assembleWeight: String = "" // 组装重量
var operator: String = "" // 操作人
var pieces: String = "" // 件数(旧字段,保留兼容)
var weight: String = "" // 重量(旧字段,保留兼容)
var destination: String = "" // 目的港
var specialCode: String = "" // 特码
var goodsName: String = "" // 品名
}

View File

@@ -244,6 +244,12 @@ interface Constant {
const val GjcBanXListActivity = "AppIntExpBox" //板箱
const val GjcGoodsListActivity = "AppIntExpGoods" //货物交接
const val GjcInspectionActivity = "AppIntExpInspection" //收运检查
const val GjcIntExpAssembleActivity = "AppIntExpAssemble" //出港组装
const val GjcAssembleAllocateActivity = "AppIntExpAssembleAllocate" //组装分配
const val GjcIntExpOutHandover = "AppIntExpOutHandover" //出库交接
const val GjcIntExpLoad = "AppIntExpLoad" //出港装载
const val GjcIntExpTally = "AppIntExpTally" //出港理货
const val GjcIntExpArrive = "AppIntExpArrive" //出港运抵
/**
* 国际进港
@@ -354,5 +360,17 @@ interface Constant {
// 运单主键ID
const val MAWB_ID = "maWbId"
// 运单前缀
const val PREFIX = "prefix"
// 运单号
const val NO = "no"
// 操作时间
const val OP_DATE = "opDate"
// Bean对象传递
const val BEAN = "bean"
}
}

View File

@@ -1,32 +1,43 @@
//package com.lukouguoji.module_base.http.net
package com.lukouguoji.module_base.http.net
import com.alibaba.fastjson.JSONObject
import com.lukouguoji.module_base.bean.AccidentVisaBean
import com.lukouguoji.module_base.bean.AirportBean
import com.lukouguoji.module_base.bean.AppUpdateResponse
import com.lukouguoji.module_base.bean.AppUpdateResponseInfo
import com.lukouguoji.module_base.bean.AssembleCompanyBean
import com.lukouguoji.module_base.bean.BaseListBean
import com.lukouguoji.module_base.bean.BaseResultBean
import com.lukouguoji.module_base.bean.BoxDetailsForCarIdBean
import com.lukouguoji.module_base.bean.CarBarBean
import com.lukouguoji.module_base.bean.CarOrUldBean
import com.lukouguoji.module_base.bean.DiBangChannelBean
import com.lukouguoji.module_base.bean.DictBean
import com.lukouguoji.module_base.bean.DictIdValueBean
import com.lukouguoji.module_base.bean.DictListBean
import com.lukouguoji.module_base.bean.DictLocationBean
import com.lukouguoji.module_base.bean.DocumentHandoverBean
import com.lukouguoji.module_base.bean.FlatcarBean
import com.lukouguoji.module_base.bean.FlightBean
import com.lukouguoji.module_base.bean.FlightFilterBean
import com.lukouguoji.module_base.bean.GbCarOrUldBean
import com.lukouguoji.module_base.bean.GjcAssembleAllocate
import com.lukouguoji.module_base.bean.GjcAssembled
import com.lukouguoji.module_base.bean.GjcBoxAddInsertBean
import com.lukouguoji.module_base.bean.GjcBoxAssembleBean
import com.lukouguoji.module_base.bean.GjcBoxDetailsBean
import com.lukouguoji.module_base.bean.GjcBoxWeighingStatisticsBean
import com.lukouguoji.module_base.bean.GjcCheckInRecord
import com.lukouguoji.module_base.bean.GjcExportLoad
import com.lukouguoji.module_base.bean.GjcGoodsAddBean
import com.lukouguoji.module_base.bean.GjcGoodsBean
import com.lukouguoji.module_base.bean.GjcGoodsDetailsBean
import com.lukouguoji.module_base.bean.GjcHandoverSheetResponse
import com.lukouguoji.module_base.bean.GjcInspectionBean
import com.lukouguoji.module_base.bean.GjcMaWb
import com.lukouguoji.module_base.bean.GjcMove
import com.lukouguoji.module_base.bean.GjcUldUseBean
import com.lukouguoji.module_base.bean.GjcWarehouse
import com.lukouguoji.module_base.bean.GjcWaybillBean
import com.lukouguoji.module_base.bean.GjcWaybillDataBean
import com.lukouguoji.module_base.bean.GjcWeighingBean
@@ -40,15 +51,11 @@ import com.lukouguoji.module_base.bean.GjjManifestBean
import com.lukouguoji.module_base.bean.GjjPackTypeBean
import com.lukouguoji.module_base.bean.GjjTallyBean
import com.lukouguoji.module_base.bean.GjjTallyDetailsBean
import com.lukouguoji.module_base.bean.JianDataBean
import com.lukouguoji.module_base.bean.PacketParseBean
import com.lukouguoji.module_base.bean.GjjTallyRecordBean
import com.lukouguoji.module_base.bean.GncAssembleListBean
import com.lukouguoji.module_base.bean.GncCunFangBean
import com.lukouguoji.module_base.bean.GncDistributionBean
import com.lukouguoji.module_base.bean.GncFuBangBean
import com.lukouguoji.module_base.bean.GjcInspectionBean
import com.lukouguoji.module_base.bean.ManifestTotalDto
import com.lukouguoji.module_base.bean.GncInspectionBean
import com.lukouguoji.module_base.bean.GncQueryBean
import com.lukouguoji.module_base.bean.GncQueryDetailsBean
@@ -62,10 +69,14 @@ import com.lukouguoji.module_base.bean.GnjStashBean
import com.lukouguoji.module_base.bean.GnjUnloadListBean
import com.lukouguoji.module_base.bean.GnjYiKuBean
import com.lukouguoji.module_base.bean.GoodsTransportBean
import com.lukouguoji.module_base.bean.JianDataBean
import com.lukouguoji.module_base.bean.LogBean
import com.lukouguoji.module_base.bean.ManifestTotalDto
import com.lukouguoji.module_base.bean.MessageBean
import com.lukouguoji.module_base.bean.MoveStashBean
import com.lukouguoji.module_base.bean.PackageBean
import com.lukouguoji.module_base.bean.PacketParseBean
import com.lukouguoji.module_base.bean.PageInfo
import com.lukouguoji.module_base.bean.SYWaybillBean
import com.lukouguoji.module_base.bean.ShouYunSyncBean
import com.lukouguoji.module_base.bean.SimpleResultBean
@@ -81,8 +92,15 @@ import com.lukouguoji.module_base.ktx.toRequestBody
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.http.*
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import retrofit2.http.PartMap
import retrofit2.http.Query
import retrofit2.http.Streaming
import retrofit2.http.Url
/**
* @author孟凡华
@@ -144,6 +162,11 @@ interface Api {
@GET("typeCode/searchDateType")
suspend fun getDictList(@Query("code") code: String): BaseResultBean<List<DictIdValueBean>>
/**
* 获取字典列表 - location接口
*/
@GET("typeCode/locationByCode")
suspend fun getDictListByLocation(@Query("code") code: String): BaseResultBean<List<DictLocationBean>>
/**
* 获取字典列表----根据用户角色选择转运类型
@@ -276,6 +299,12 @@ interface Api {
@POST("eqm/uld/queryUld")
suspend fun getUldDetails(@Query("id") id: String): BaseResultBean<ULDBean>
/**
* 根据ULD编号查询ULD信息新接口
*/
@GET("eqm/uld/queryUld")
suspend fun queryUldByCode(@Query("uld") uld: String): BaseResultBean<ULDBean>
/**
* 获取运单信息 - 国际出
*/
@@ -405,7 +434,10 @@ interface Api {
* 接口路径: /IntExpCheckInCheck/back
*/
@POST("IntExpCheckInCheck/back")
suspend fun backGjcInspection(@Body data: RequestBody): BaseResultBean<Boolean>
suspend fun backGjcInspection(
@Query("reason") reason: String,
@Body data: RequestBody
): BaseResultBean<Boolean>
/**
* 条件查询-国际出港-收运审核-统计数据(总件数、总重量、运单总数)
@@ -414,6 +446,80 @@ interface Api {
@POST("IntExpCheckInCheck/pageQueryTotal")
suspend fun getGjcInspectionTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际出港收运审核-交接单-获取交接单内容
* 接口路径: /IntExpCheckInCheck/queryHandoverSheet
*/
@GET("IntExpCheckInCheck/queryHandoverSheet")
suspend fun queryHandoverSheet(@Query("maWbId") maWbId: Long): BaseResultBean<GjcHandoverSheetResponse>
/**
* 国际出港收运审核-交接单-保存
* 接口路径: /IntExpCheckInCheck/saveHandoverSheet
*/
@POST("IntExpCheckInCheck/saveHandoverSheet")
suspend fun saveHandoverSheet(@Body data: RequestBody): BaseResultBean<Long>
///////////////////////////////////////////////////////////////////////////
// 国际出 - 查询
///////////////////////////////////////////////////////////////////////////
/**
* 国际出港查询-分页查询
* 接口路径: /IntExpSearch/pageQuery
*/
@POST("IntExpSearch/pageQuery")
suspend fun getGjcQueryList(@Body data: RequestBody): PageInfo<GjcMaWb>
/**
* 国际出港查询-分页合计统计
* 接口路径: /IntExpSearch/pageQueryTotal
*/
@POST("IntExpSearch/pageQueryTotal")
suspend fun getGjcQueryTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际出港运单修改
* 接口路径: /IntExpSearch/update
*/
@POST("IntExpSearch/update")
suspend fun updateGjcMaWb(@Body data: RequestBody): BaseResultBean<Any>
/**
* 国际出港查询-详情
* 接口路径: /IntExpSearch/detail
* 参数: maWbId (Long)
*/
@POST("IntExpSearch/detail")
suspend fun getGjcQueryDetails(@Query("maWbId") maWbId: Long): BaseResultBean<Map<String, Any>>
/**
* 国际出港组装分配-分页查询
* 接口路径: /IntExpAssemble/allocate/pageQuery
*/
@POST("IntExpAssemble/allocate/pageQuery")
suspend fun getGjcAssembleAllocateList(@Body params: RequestBody): PageInfo<GjcAssembleAllocate>
/**
* 国际出港组装分配-分页合计统计
* 接口路径: /IntExpAssemble/allocate/pageQueryTotal
*/
@POST("IntExpAssemble/allocate/pageQueryTotal")
suspend fun getGjcAssembleAllocateTotal(@Body params: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际出港组装分配-执行分配操作
* 接口路径: /IntExpAssemble/allocate
*/
@POST("IntExpAssemble/allocate")
suspend fun allocateAssemble(@Body params: RequestBody): BaseResultBean<Boolean>
/**
* 获取组装公司下拉列表
* 接口路径: /typeCode/assembleCompany
*/
@POST("typeCode/assembleCompany")
suspend fun getAssembleCompanyList(): BaseResultBean<List<AssembleCompanyBean>>
/**
* 国际出港板箱过磅-分页搜索
* 接口路径: /IntExpWeighting/pageQuery
@@ -442,6 +548,263 @@ interface Api {
@POST("IntExpWeighting/weight")
suspend fun submitGjcBoxWeighing(@Body data: RequestBody): BaseResultBean<SimpleResultBean>
// ==================== 国际出港-出港组装 ====================
/**
* 国际出港组装-分页查询已组装的ULD列表
* 接口路径: /IntExpAssemble/pageQuery
*/
@POST("IntExpAssemble/pageQuery")
suspend fun getIntExpAssembleList(@Body data: RequestBody): BaseListBean<GjcUldUseBean>
/**
* 国际出港组装-分页合计统计数据
* 接口路径: /IntExpAssemble/pageQueryTotal
*/
@POST("IntExpAssemble/pageQueryTotal")
suspend fun getIntExpAssembleTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际出港组装-根据板箱组装记录查询运单明细
* 接口路径: /IntExpAssemble/queryAssembled
* @param data 请求参数useIdULD使用ID
*/
@POST("IntExpAssemble/queryAssembled")
suspend fun getIntExpAssembleWaybillDetails(@Body data: RequestBody): BaseResultBean<MutableList<GjcWarehouse>>
/**
* 国际出港组装-删除记录
* 接口路径: /IntExpAssemble/delete
* @param ids ULD使用记录ID多个用逗号分隔
*/
@POST("IntExpAssemble/delete")
suspend fun deleteIntExpAssemble(@Query("ids") ids: String): BaseResultBean<SimpleResultBean>
/**
* 国际出港组装-回填重量
* 接口路径: /IntExpAssemble/backfillWeight
* @param data 请求参数idsULD使用记录ID逗号分隔、weight重量
*/
@POST("IntExpAssemble/backfillWeight")
suspend fun backfillIntExpAssembleWeight(@Body data: RequestBody): BaseResultBean<SimpleResultBean>
/**
* 国际出港组装 - 获取组装人列表
* 接口路径: /IntExpAssemble/pageQueryAssembler
*/
@GET("IntExpAssemble/pageQueryAssembler")
suspend fun getIntExpAssemblerList(): BaseResultBean<List<String>>
/**
* 国际出港组装 - 查询待组装运单列表
* 接口路径: /IntExpAssemble/queryWaitingAssemble
*/
@GET("IntExpAssemble/queryWaitingAssemble")
suspend fun queryWaitingAssemble(@Query("wbNo") wbNo: String): BaseResultBean<MutableList<GjcWarehouse>>
/**
* 国际出港组装 - 根据全ULD查询ULD状态和耗材重量
* 接口路径: /IntExpAssemble/getUld
* @param uld ULD编号
* @return ULD信息包含status0正常1故障和consumeWeight耗材重量
*/
@GET("IntExpAssemble/getUld")
suspend fun getUldWithConsumeWeight(@Query("uld") uld: String): BaseResultBean<GjcUldUseBean>
/**
* 国际出港组装 - 卸货
* 接口路径: /IntExpAssemble/drop
* @param data 请求参数abPc组装件数、abWeight组装重量、consumeWeight耗材重量
* ldId组装人、loadArea组装区、useInfoULD信息、wbInfo运单信息、userId用户ID
*/
@POST("IntExpAssemble/drop")
suspend fun assembleDropCargo(@Body data: RequestBody): BaseResultBean<GjcWarehouse>
/**
* 国际出港组装 - 装货
* 接口路径: /IntExpAssemble/assemble
* @param data 请求参数abPc组装件数、abWeight组装重量、consumeWeight耗材重量
* ldId组装人、loadArea组装区、useInfoULD信息、wbInfo运单信息、userId用户ID
*/
@POST("IntExpAssemble/assemble")
suspend fun assembleLoadCargo(@Body data: RequestBody): BaseResultBean<GjcWarehouse>
/**
* 国际出港组装 - 查询已组装的ULD列表
* 接口路径: /IntExpAssemble/queryAssembled
* @param data 请求参数fno航班号必填、fdate航班日期必填
* loadArea组装位置必填、uldULD编号可选
* @return 返回已组装的ULD列表包含ULD信息和运单列表
*/
@POST("IntExpAssemble/queryAssembled")
suspend fun getAssembledList(@Body data: RequestBody): BaseResultBean<List<GjcAssembled>>
/**
* 国际出港组装 - 根据ULD查询已组装的运单列表
* 接口路径: /IntExpAssemble/queryAssembledByUld
* @param data 请求参数GjcUldUseBean对象包含useId、uld等信息
* @return 返回该ULD下的运单列表
*/
@POST("IntExpAssemble/queryAssembledByUld")
suspend fun getAssembledWaybillsByUld(@Body data: RequestBody): BaseResultBean<List<GjcWarehouse>>
/**
* 国际出港出库交接-分页查询
* 接口路径: /IntExpOutHandover/pageQuery
*/
@POST("IntExpOutHandover/pageQuery")
suspend fun getIntExpOutHandoverList(@Body data: RequestBody): BaseListBean<GjcUldUseBean>
/**
* 国际出港出库交接-分页合计
* 接口路径: /IntExpOutHandover/pageQueryTotal
*/
@POST("IntExpOutHandover/pageQueryTotal")
suspend fun getIntExpOutHandoverTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际出港出库交接-完成交接
* 接口路径: /IntExpOutHandover/handover
* @param data 请求参数选中的ULD列表
*/
@POST("IntExpOutHandover/handover")
suspend fun completeHandover(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港-出港装载 分页查询
* 接口路径: /IntExpLoad/pageQuery
*/
@POST("IntExpLoad/pageQuery")
suspend fun getIntExpLoadList(@Body data: RequestBody): BaseListBean<GjcExportLoad>
/**
* 国际出港-出港装载 分页合计
* 接口路径: /IntExpLoad/pageQueryTotal
*/
@POST("IntExpLoad/pageQueryTotal")
suspend fun getIntExpLoadTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际出港-出港装载 状态重置
* 接口路径: /IntExpLoad/resetDeclare
*/
@POST("IntExpLoad/resetDeclare")
suspend fun resetDeclare(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港-出港装载 装载申报
* 接口路径: /IntExpLoad/declare
*/
@POST("IntExpLoad/declare")
suspend fun declareLoad(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港运抵-分页列表
* 接口路径: /IntExpArrive/pageQuery
*/
@POST("IntExpArrive/pageQuery")
suspend fun getIntExpArriveList(@Body data: RequestBody): BaseListBean<GjcMaWb>
/**
* 国际出港运抵-分页合计
* 接口路径: /IntExpArrive/pageQueryTotal
*/
@POST("IntExpArrive/pageQueryTotal")
suspend fun getIntExpArriveTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际出港运抵-重置运抵申报状态
* 接口路径: /IntExpArrive/resetDeclare
* @param data 请求参数:选中的运单列表
*/
@POST("IntExpArrive/resetDeclare")
suspend fun resetArriveDeclare(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港运抵-运抵申报(分单+主单)
* 接口路径: /IntExpArrive/declare
* @param data 请求参数:选中的运单列表
*/
@POST("IntExpArrive/declare")
suspend fun arriveDeclare(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港-出港理货-列表查询
* 接口路径: /IntExpTally/pageQuery
* 返回: PageInfo<GjcMaWb>
*/
@POST("IntExpTally/pageQuery")
suspend fun getIntExpTallyList(@Body data: RequestBody): BaseListBean<GjcMaWb>
/**
* 国际出港-出港理货-统计查询
* 接口路径: /IntExpTally/pageQueryTotal
* 返回: ManifestTotalDto合计票数、总件数、总重量
*/
@POST("IntExpTally/pageQueryTotal")
suspend fun getIntExpTallyTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际出港-出港理货-理货申报
* 接口路径: /IntExpTally/declare
* @param data 请求参数选中的GjcMaWb列表
*/
@POST("IntExpTally/declare")
suspend fun declareTally(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港-出港理货-状态重置
* 接口路径: /IntExpTally/resetDeclare
* @param data 请求参数选中的GjcMaWb列表
*/
@POST("IntExpTally/resetDeclare")
suspend fun resetTallyDeclare(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港-出港理货-删除理货申报
* 接口路径: /IntExpTally/deleteDeclare
* @param data 请求参数GjcDeclareParam
*/
@POST("IntExpTally/deleteDeclare")
suspend fun deleteTallyDeclare(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港-出港装载-删除装载申报
* 接口路径: /IntExpLoad/deleteDeclare
* @param data 请求参数GjcDeclareParam
*/
@POST("IntExpLoad/deleteDeclare")
suspend fun deleteLoadDeclare(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港-出港运抵-删除运抵申报
* 接口路径: /IntExpArrive/deleteDeclare
* @param data 请求参数GjcDeclareParam
*/
@POST("IntExpArrive/deleteDeclare")
suspend fun deleteArriveDeclare(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港移库-分页查询
* 接口路径: /IntExpMove/pageQuery
*/
@POST("IntExpMove/pageQuery")
suspend fun getIntExpMoveList(@Body data: RequestBody): BaseListBean<GjcMove>
/**
* 国际出港移库-分页合计
* 接口路径: /IntExpMove/pageQueryTotal
*/
@POST("IntExpMove/pageQueryTotal")
suspend fun getIntExpMoveTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
/**
* 国际出港移库-批量移库
* 接口路径: /IntExpMove/move
*/
@POST("IntExpMove/move")
suspend fun submitIntExpMove(@Body data: RequestBody): BaseResultBean<SimpleResultBean>
/**
* 国际出港待计重-分页搜索
* 接口路径: /IntExpCheckIn/pageQuery
@@ -456,6 +819,23 @@ interface Api {
@POST("IntExpCheckIn/pageQueryTotal")
suspend fun getGjcWeighingStatistics(@Body data: RequestBody): BaseResultBean<GjcWeighingStatisticsBean>
/**
* 国际出港待计重-开始计重-根据wbId查询详情
* 接口路径: /IntExpCheckIn/queryWbById
* @param maWbId 运单主键ID
*/
@POST("IntExpCheckIn/queryWbById")
suspend fun getIntExpCheckInWbById(@Query("maWbId") maWbId: Long): BaseResultBean<GjcMaWb>
/**
* 国际出港待计重-开始计重-根据运单id查询实时计重数据
* 接口路径: /IntExpCheckIn/queryRecordByWh
* @param maWbId 运单主键ID
* @return 返回GjcCheckInRecord,包含实时的pc、weight、volume
*/
@POST("IntExpCheckIn/queryRecordByWh")
suspend fun getIntExpRealTimeRecord(@Query("maWbId") maWbId: Long): BaseResultBean<GjcCheckInRecord>
/**
* 国际出港计重记录-分页搜索
* 接口路径: /IntExpCheckIn/checked/pageQuery
@@ -477,6 +857,29 @@ interface Api {
@POST("IntExpCheckIn/completeCheckIn")
suspend fun completeCheckIn(@Body data: RequestBody): BaseResultBean<Boolean>
/**
* 国际出港待计重-开始计重-分托计重
* 接口路径: /IntExpCheckIn/splitCheckIn
*/
@POST("IntExpCheckIn/splitCheckIn")
suspend fun splitCheckIn(@Body data: RequestBody): BaseResultBean<Long>
/**
* 国际出港计重明细-根据运单号查询所有计重记录
* 接口路径: /IntExpCheckIn/listRecordByWh
* 参数: GjcWarehouse对象(prefix, no, opDate)
*/
@POST("IntExpCheckIn/listRecordByWh")
suspend fun getGjcCheckInRecordList(@Body data: RequestBody): BaseResultBean<List<GjcCheckInRecord>>
/**
* 国际出港计重明细-批量更新计重记录
* 接口路径: /IntExpCheckIn/updateRecordList
* 参数: List<GjcCheckInRecord>
*/
@POST("IntExpCheckIn/updateRecordList")
suspend fun updateGjcCheckInRecordList(@Body data: List<GjcCheckInRecord>): BaseResultBean<Boolean>
///////////////////////////////////////////////////////////////////////////
// 国际进-电报解析
///////////////////////////////////////////////////////////////////////////
@@ -692,6 +1095,12 @@ interface Api {
@POST("flt/queryFlightById")
suspend fun getFlightDetails(@Query("id") id: String): BaseResultBean<FlightBean>
/**
* 根据航班日期和航班号查询航班
*/
@POST("flt/queryFlight")
suspend fun queryFlightByDateAndNo(@Body data: RequestBody): BaseResultBean<FlightBean>
/**
* 获取航班目的站、经停站
*/
@@ -969,6 +1378,7 @@ interface Api {
*/
@POST
suspend fun getWbNoList(@Url url: String, @Body data: RequestBody): BaseResultBean<List<String>>
/**
* 获取-国内出港-分配-列表
*/
@@ -987,6 +1397,7 @@ interface Api {
*/
@POST("flt/searchHandoverByFid")
suspend fun getHandover(@Query("fid") fid: String): BaseResultBean<FlightBean>
/**
* 获取-国内出港-存放-列表
*/
@@ -1043,7 +1454,6 @@ interface Api {
fun queryWbDetailById(@Query("wbId") wbId: Int): BaseResultBean<GncShouYunBean>
/**
* 获取-货物转运-列表
*/
@@ -1091,13 +1501,19 @@ interface Api {
*/
@POST("DomTransportLog/search")
suspend fun getTransportLogList(@Body data: RequestBody): BaseListBean<TransportLogBean>
/**
* 国内 单证交接 列表
*/
@POST("flt/searchHandoverPage")
suspend fun getDocumentHandoverList(@Body data: RequestBody): BaseListBean<DocumentHandoverBean>
/**
* 获取删除原因列表
*/
@POST("typeCode/delReason")
suspend fun getDelReasonList(@Query("needFormat") needFormat: Boolean = true): BaseResultBean<List<DictBean>>
@GET("file/verifyVersion")
suspend fun getAppUpdate(@Query("versionCode") versionCode : Int): AppUpdateResponse<AppUpdateResponseInfo>
suspend fun getAppUpdate(@Query("versionCode") versionCode: Int): AppUpdateResponse<AppUpdateResponseInfo>
}

View File

@@ -1,6 +1,7 @@
package com.lukouguoji.module_base.ktx
import android.text.InputFilter
import android.text.Spanned
import android.widget.EditText
import androidx.databinding.BindingAdapter
@@ -12,4 +13,47 @@ fun setTextAllCaps(et: EditText, allCaps: Boolean) {
} else {
et.filters = emptyArray<InputFilter>()
}
}
/**
* 大写字母和数字输入过滤器
* 只允许输入大写字母(A-Z)和数字(0-9),小写字母自动转为大写
*/
class UpperCaseAlphanumericInputFilter : InputFilter {
override fun filter(
source: CharSequence?,
start: Int,
end: Int,
dest: Spanned?,
dstart: Int,
dend: Int
): CharSequence? {
if (source.isNullOrEmpty()) return null
val filtered = StringBuilder()
for (i in start until end) {
val char = source[i]
// 只允许ASCII字母(A-Z, a-z)和数字(0-9)
if (char in 'A'..'Z' || char in 'a'..'z' || char in '0'..'9') {
// 自动转为大写
filtered.append(char.uppercaseChar())
}
}
// 如果过滤后的内容与原内容相同,返回null表示不修改
// 否则返回过滤后的内容
return if (filtered.toString() == source.subSequence(start, end).toString()) {
null
} else {
filtered.toString()
}
}
}
/**
* 为 EditText 设置大写字母和数字输入过滤器
* 使用方式: editText.setUpperCaseAlphanumericFilter()
*/
fun EditText.setUpperCaseAlphanumericFilter() {
this.filters = arrayOf(UpperCaseAlphanumericInputFilter())
}

View File

@@ -122,12 +122,15 @@ object ARouterConstants {
const val ACTIVITY_URL_GJC_WEIGHING_LIST = "/gjc/GjcWeighingListActivity" //国际出港模块 出港计重
const val ACTIVITY_URL_GJC_WEIGHING_START = "/gjc/GjcWeighingStartActivity" //国际出港模块 开始计重
const val ACTIVITY_URL_GJC_WEIGHING_RECORD_LIST = "/gjc/GjcWeighingRecordListActivity" //国际出港模块 计重记录
const val ACTIVITY_URL_GJC_WEIGHING_RECORD_DETAILS = "/gjc/GjcWeighingRecordDetailsActivity" //国际出港模块 计重明细
const val ACTIVITY_URL_GJC_WARE_HOUSE = "/gjc/GjcWareHouseActivity" //国际出港模块 仓库
const val ACTIVITY_URL_GJC_WARE_HOUSE_INFO = "/gjc/GjcWareHouseInfoActivity" //国际出港模块 仓库详情
const val ACTIVITY_URL_GJC_QUERY_LIST = "/gjc/GjcQueryListActivity" //国际出港模块 查询
const val ACTIVITY_URL_GJC_QUERY_INFO = "/gjc/GjcQueryInfoActivity" //国际出港模块 详情
const val ACTIVITY_URL_GJC_QUERY_DETAILS = "/gjc/GjcQueryDetailsActivity" //国际出港模块 查询详情
const val ACTIVITY_URL_GJC_QUERY_EDIT = "/gjc/GjcQueryEditActivity" //国际出港模块 运单修改
const val ACTIVITY_URL_GJC_YI_KU = "/gjc/GjcYiKuListActivity" //国际出港 移库
const val ACTIVITY_URL_GJC_BOX_ASSEMBLE = "/gjc/GjcBoxAssembleListActivity" //国际出港 板箱组装
@@ -135,6 +138,14 @@ object ARouterConstants {
const val ACTIVITY_URL_GJC_INSPECTION = "/gjc/GjcInspectionActivity" //国际出港 收运检查
const val ACTIVITY_URL_GJC_INSPECTION_DETAILS = "/gjc/GjcInspectionDetailsActivity" //国际出港 收运检查详情
const val ACTIVITY_URL_GJC_HANDOVER = "/gjc/GjcHandoverActivity" //国际出港 货物交接单
const val ACTIVITY_URL_INT_EXP_ASSEMBLE = "/gjc/IntExpAssembleActivity" //国际出港 出港组装
const val ACTIVITY_URL_INT_EXP_ASSEMBLE_START = "/gjc/IntExpAssembleStartActivity" //国际出港 开始组装
const val ACTIVITY_URL_INT_EXP_MOVE = "/gjc/IntExpMoveActivity" //国际出港 出港移库
const val ACTIVITY_URL_GJC_ASSEMBLE_ALLOCATE = "/gjc/GjcAssembleAllocateActivity" //国际出港 组装分配
const val ACTIVITY_URL_INT_EXP_OUT_HANDOVER = "/gjc/IntExpOutHandoverActivity" //国际出港 出库交接
const val ACTIVITY_URL_INT_EXP_LOAD = "/gjc/IntExpLoadActivity" //国际出港 出港装载
const val ACTIVITY_URL_INT_EXP_TALLY = "/gjc/IntExpTallyActivity" //国际出港 出港理货
const val ACTIVITY_URL_INT_EXP_ARRIVE = "/gjc/IntExpArriveActivity" //国际出港 出港运抵
///////////////// 国际进港模块
/**

View File

@@ -152,4 +152,145 @@ fun setTextAllCaps(layout: PadDataLayout, textAllCaps: Boolean) {
} else {
layout.et.filters = emptyArray<InputFilter>()
}
}
// ========== PadDataLayoutNew BindingAdapters ==========
@BindingAdapter(
"type",
"title",
"titleLength",
"hint",
"required",
"icon",
requireAll = false
)
fun setDataLayoutDataNew(
dataLayout: PadDataLayoutNew,
type: DataLayoutType?,
title: String?,
titleLength: Int?,
hint: String?,
required: Boolean?,
icon: Any?,
) {
type?.let {
dataLayout.type = it
}
title?.let {
dataLayout.tvTitle.text = title
}
titleLength?.let {
dataLayout.titleLength = titleLength
}
required?.let {
dataLayout.required = it
}
hint?.let {
dataLayout.hint = hint
}
dataLayout.icon = icon
}
@BindingAdapter(
"value",
requireAll = false
)
fun setSearchLayoutDataValueNew(
layout: PadDataLayoutNew,
value: String?,
) {
value?.let {
layout.value = value
}
}
@BindingAdapter(
"enable",
requireAll = false
)
fun setSearchLayoutDataEnableNew(
layout: PadDataLayoutNew,
enable: Boolean?,
) {
enable?.let {
layout.enable = enable
}
}
@BindingAdapter(
"list",
requireAll = false
)
fun setSearchLayoutDataListNew(
layout: PadDataLayoutNew,
list: List<KeyValue>?,
) {
list?.let {
layout.list = list
}
}
@BindingAdapter(
"inputHeight",
requireAll = false
)
fun setSearchLayoutInputHeightNew(
layout: PadDataLayoutNew,
inputHeight: Int?,
) {
inputHeight?.let {
ViewUtils.setHeight(layout.llContainer, SizeUtils.dp2px(it.toFloat()))
layout.et.setRawInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE)
layout.et.gravity = Gravity.TOP
layout.et.isSingleLine = false
layout.et.isHorizontalScrollBarEnabled = false
ViewUtils.setPadding(layout.et, SizeUtils.dp2px(10f))
}
}
/**
* 设置最大长度
* 仅对Input类型有效
*/
@BindingAdapter(
"maxLength",
requireAll = false
)
fun setSearchLayoutMaxLengthNew(
layout: PadDataLayoutNew,
maxLength: Int?,
) {
maxLength?.let {
EditTextUtils.setMaxLength(layout.et, maxLength)
}
}
@InverseBindingAdapter(attribute = "value", event = "valueAttrChanged")
fun getDataLayoutValueNew(dataLayout: PadDataLayoutNew): String {
return dataLayout.value
}
@BindingAdapter("valueAttrChanged", requireAll = false)
fun setDataLayoutValueAttrChangedNew(dataLayout: PadDataLayoutNew, listener: InverseBindingListener) {
dataLayout.onChangeListener = listener
}
@BindingAdapter("setOnIconClickListener", requireAll = false)
fun setOnIconClickListenerNew(layout: PadDataLayoutNew, listener: View.OnClickListener) {
layout.iv.setOnClickListener(listener)
}
@BindingAdapter("setRefreshCallBack", requireAll = false)
fun setRefreshCallBackNew(layout: PadDataLayoutNew, listener: (() -> Unit)?) {
layout.refreshCallBack = listener
}
@BindingAdapter("setTextAllCaps", requireAll = false)
fun setTextAllCapsNew(layout: PadDataLayoutNew, textAllCaps: Boolean) {
if (textAllCaps) {
layout.et.filters = arrayOf<InputFilter>(InputFilter.AllCaps())
} else {
layout.et.filters = emptyArray<InputFilter>()
}
}

View File

@@ -0,0 +1,261 @@
package com.lukouguoji.module_base.ui.weight.data.layout
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Spinner
import android.widget.TextView
import androidx.core.widget.doOnTextChanged
import androidx.databinding.InverseBindingListener
import androidx.databinding.ObservableArrayList
import androidx.databinding.ObservableBoolean
import androidx.databinding.ObservableField
import androidx.databinding.ObservableInt
import com.lukouguoji.module_base.R
import com.lukouguoji.module_base.adapter.bindAdapter
import com.lukouguoji.module_base.adapter.bindOnSelected
import com.lukouguoji.module_base.adapter.completeSpace
import com.lukouguoji.module_base.adapter.loadImage
import com.lukouguoji.module_base.adapter.setSelectedItem
import com.lukouguoji.module_base.adapter.visible
import com.lukouguoji.module_base.interfaces.IOnFocusChangeListener
import com.lukouguoji.module_base.interfaces.IOnSpinnerSelected
import com.lukouguoji.module_base.ktx.formatDate
import com.lukouguoji.module_base.ktx.getActivity
import com.lukouguoji.module_base.ktx.loge
import com.lukouguoji.module_base.util.Common
import dev.utils.app.info.KeyValue
import dev.utils.common.ReflectUtils
import java.util.Calendar
import kotlin.properties.Delegates
class PadDataLayoutNew : FrameLayout {
constructor(context: Context?) : super(context!!)
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context!!,
attrs,
defStyleAttr
)
var onChangeListener: InverseBindingListener? = null
var llContainer: LinearLayout by Delegates.notNull()
var ll: LinearLayout by Delegates.notNull()
var tvTitle: TextView by Delegates.notNull()
var tvM: TextView by Delegates.notNull()
var tv: TextView by Delegates.notNull()
var tvSpinner: TextView by Delegates.notNull()
var et: EditText by Delegates.notNull()
var spinner: Spinner by Delegates.notNull()
var iv: ImageView by Delegates.notNull()
var type = DataLayoutType.INPUT
set(value) {
field = value
setForType()
}
var enable = true
set(value) {
field = value
setForEnable()
}
var titleLength = 0
set(value) {
field = value
if (value > 0) {
completeSpace(tvTitle, value, false)
}
}
var value = ""
set(value) {
if (field == value) {
return
}
field = value
onValueSet()
onChangeListener?.onChange()
}
var show = ""
var title = ""
set(value) {
field = value
tvTitle.text = value
}
var hint = ""
set(value) {
field = value
et.hint = value
bindAdapter(spinner, list, hint)
}
var required = false
set(value) {
field = value
tvM.visibility = if (value) VISIBLE else INVISIBLE
}
var list = emptyList<KeyValue>()
set(value) {
field = value
bindAdapter(spinner, value, hint)
onValueSet()
}
var icon: Any? = null
set(value) {
field = value
visible(iv, value)
loadImage(iv, value)
}
var inputType: Int = android.text.InputType.TYPE_CLASS_TEXT
set(value) {
field = value
et.inputType = value
}
var valueTextColor: Int = 0
set(value) {
field = value
if (value != 0) {
tv.setTextColor(value)
et.setTextColor(value)
}
}
/**
* 刷新事件回调
*/
var refreshCallBack: (() -> Unit)? = {}
// 选择日期
private val dateClick: (v: View) -> Unit = {
if (enable) {
Common.onYearMonthDay(context.getActivity(), value) { year, month, day ->
val calendar = Calendar.getInstance()
calendar.set(year, month - 1, day)
value = calendar.time.formatDate()
refreshCallBack?.invoke()
}
}
}
init {
initView()
}
private fun initView() {
val view = inflate(context, R.layout.layout_pad_data_new, this)
llContainer = view.findViewById(R.id.ll_container)
ll = view.findViewById(R.id.ll)
tvTitle = view.findViewById(R.id.tv_title)
tvM = view.findViewById(R.id.tv_m)
tv = view.findViewById(R.id.tv)
tvSpinner = view.findViewById(R.id.tv_spinner)
et = view.findViewById(R.id.et)
spinner = view.findViewById(R.id.spinner)
iv = view.findViewById(R.id.iv)
et.doOnTextChanged { text, _, _, _ ->
value = text.toString()
}
bindOnSelected(spinner, object : IOnSpinnerSelected {
override fun onSelected(position: Int) {
value = list.getOrNull(position)?.value ?: ""
refreshCallBack?.invoke()
}
})
// 监听输入框焦点变化
com.lukouguoji.module_base.adapter.setOnFocusChangeListener(
et, object : IOnFocusChangeListener {
override fun onFocusChange(hasFocus: Boolean) {
if (!hasFocus) {
refreshCallBack?.invoke()
}
}
})
setForType()
}
private fun setForEnable() {
ll.isEnabled = enable
et.isEnabled = enable
spinner.isEnabled = enable
tv.isEnabled = enable
setForType()
onValueSet()
}
private fun setForType() {
when (type) {
DataLayoutType.INPUT -> {
et.visibility = VISIBLE
spinner.visibility = GONE
tvSpinner.visibility = GONE
tv.visibility = GONE
setOnClickListener(null)
}
DataLayoutType.SPINNER -> {
et.visibility = GONE
tv.visibility = GONE
if (enable) {
spinner.visibility = VISIBLE
tvSpinner.visibility = GONE
} else {
tvSpinner.visibility = VISIBLE
spinner.visibility = GONE
}
setOnClickListener(null)
}
DataLayoutType.DATE -> {
tv.visibility = VISIBLE
et.visibility = GONE
spinner.visibility = GONE
tvSpinner.visibility = GONE
setOnClickListener(dateClick)
}
}
}
private fun onValueSet() {
if (et.text.toString() != value) {
et.setText(value)
}
if (tv.text.toString() != value) {
tv.text = value
}
tvSpinner.text = list.find { b -> b.value == value }?.key ?: value
if (value.isNotEmpty() && list.isNotEmpty()) {
val index = list.indexOfFirst { b -> b.value == value }
if (index >= 0) {
spinner.post {
spinner.setSelection(index)
}
}
}
}
internal fun setOnValueChangeListener(listener: InverseBindingListener) {
if (onChangeListener == null) {
this.onChangeListener = listener
}
}
}

View File

@@ -0,0 +1,263 @@
package com.lukouguoji.module_base.ui.weight.search.layout
import android.content.Context
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Spinner
import android.widget.TextView
import androidx.core.widget.doOnTextChanged
import androidx.databinding.InverseBindingListener
import com.lukouguoji.module_base.R
import com.lukouguoji.module_base.adapter.bindAdapter
import com.lukouguoji.module_base.adapter.bindOnSelected
import com.lukouguoji.module_base.adapter.loadImage
import com.lukouguoji.module_base.adapter.visible
import com.lukouguoji.module_base.interfaces.IOnFocusChangeListener
import com.lukouguoji.module_base.interfaces.IOnSpinnerSelected
import com.lukouguoji.module_base.ktx.formatDate
import com.lukouguoji.module_base.ktx.getActivity
import com.lukouguoji.module_base.ktx.loge
import com.lukouguoji.module_base.ktx.tryCatch
import com.lukouguoji.module_base.util.Common
import dev.utils.app.info.KeyValue
import java.util.Calendar
import kotlin.properties.Delegates
class PadSearchLayoutNew : LinearLayout {
constructor(context: Context?) : super(context!!)
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context!!,
attrs,
defStyleAttr
)
var onChangeListener: InverseBindingListener? = null
var ll: LinearLayout by Delegates.notNull()
var tvM: TextView by Delegates.notNull()
var tv: TextView by Delegates.notNull()
var et: EditText by Delegates.notNull()
var spinner: Spinner by Delegates.notNull()
var iv: ImageView by Delegates.notNull()
var type = SearchLayoutType.INPUT
set(value) {
field = value
setForType()
onValueSet()
}
var enable = true
set(value) {
field = value
ll.isEnabled = value
et.isEnabled = value
spinner.isEnabled = value
}
var value = ""
set(value) {
if (field == value) {
return
}
field = value
onValueSet()
onChangeListener?.onChange()
}
var hint = ""
set(value) {
field = value
et.hint = value
tv.hint = value
bindAdapter(spinner, list, hint)
}
var list = emptyList<KeyValue>()
set(value) {
field = value
bindAdapter(spinner, value, hint)
onValueSet()
}
var required = false
set(value) {
field = value
tvM.visibility = if (value) VISIBLE else GONE
}
var icon: Any? = null
set(value) {
field = value
visible(iv, value)
loadImage(iv, value)
}
// 选择日期
private val dateClick: (v: View) -> Unit = {
if (enable) {
Common.onYearMonthDay(context.getActivity(), value) { year, month, day ->
val calendar = Calendar.getInstance()
calendar.set(year, month - 1, day)
value = calendar.time.formatDate()
refreshCallBack?.invoke()
}
}
}
/**
* 刷新事件回调
*/
var refreshCallBack: (() -> Unit)? = {}
var listRefreshCallBack: (() -> Unit)? = {}
/**
* 搜索事件回调(回车键或搜索按钮触发)
*/
var searchCallBack: (() -> Unit)? = null
///////////////////////////////////////////////////////////////////////////
// 方法区
///////////////////////////////////////////////////////////////////////////
init {
val view = inflate(context, R.layout.layout_pad_search_new, this)
ll = view.findViewById(R.id.ll)
tvM = view.findViewById(R.id.tv_m)
tv = view.findViewById(R.id.tv)
et = view.findViewById(R.id.et)
spinner = view.findViewById(R.id.spinner)
iv = view.findViewById(R.id.iv)
et.doOnTextChanged { text, _, _, _ ->
value = text.toString()
}
// 回车键监听 - 触发搜索
et.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_SEARCH ||
actionId == EditorInfo.IME_ACTION_DONE ||
(event?.keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN)) {
// 优先调用searchCallBack如果没有则调用refreshCallBack
searchCallBack?.invoke() ?: refreshCallBack?.invoke()
true
} else {
false
}
}
bindOnSelected(spinner, object : IOnSpinnerSelected {
override fun onSelected(position: Int) {
value = list.getOrNull(position)?.value ?: ""
refreshCallBack?.invoke()
}
})
// 监听输入框焦点变化
com.lukouguoji.module_base.adapter.setOnFocusChangeListener(
et, object : IOnFocusChangeListener {
override fun onFocusChange(hasFocus: Boolean) {
if (!hasFocus) {
refreshCallBack?.invoke()
}
}
})
setForType()
}
private fun setForType() {
when (type) {
SearchLayoutType.INPUT -> {
et.visibility = VISIBLE
spinner.visibility = GONE
tv.visibility = GONE
setOnClickListener(null)
}
SearchLayoutType.INTEGER -> {
et.visibility = VISIBLE
spinner.visibility = GONE
tv.visibility = GONE
setOnClickListener(null)
}
SearchLayoutType.SPINNER -> {
spinner.visibility = VISIBLE
et.visibility = GONE
tv.visibility = GONE
setOnClickListener(null)
}
SearchLayoutType.DATE -> {
tv.visibility = VISIBLE
spinner.visibility = GONE
et.visibility = GONE
setOnClickListener(dateClick)
}
}
}
private fun onValueSet() {
when (type) {
SearchLayoutType.INPUT -> {
if (et.text.toString() != value) {
et.setText(value)
}
}
SearchLayoutType.INTEGER -> {
var stringAnInt = isStringAnInt(et.text.toString())
if (stringAnInt && et.text.toString() != value ) {
et.setText(value)
}
}
SearchLayoutType.SPINNER -> {
if (value.isNotEmpty()) {
val position = list.indexOfFirst { it.value == value }
if (position >= 0) {
spinner.setSelection(position)
}
}
}
SearchLayoutType.DATE -> {
tv.text = value
}
}
}
/**
* 判断是否是数字
*/
fun isStringAnInt(str: String?): Boolean {
return str?.toIntOrNull() != null
}
/**
* 重置数据
*/
fun reset() {
value = ""
if (type == SearchLayoutType.SPINNER && list.isNotEmpty()) {
spinner.setSelection(
if (hint.isEmpty()) list.size - 1 else list.size
)
}
}
}

View File

@@ -138,4 +138,154 @@ fun setInputWaybill(layout: PadSearchLayout, isWaybill: Boolean) {
}
}
}
}
/**
* 设置搜索监听器(回车键或搜索按钮触发)
*/
@BindingAdapter("setOnSearchListener", requireAll = false)
fun setSearchLayoutNewOnSearchListener(layout: PadSearchLayoutNew, listener: (() -> Unit)?) {
layout.searchCallBack = listener
}
/**
* 设置搜索图标点击为搜索功能
*/
@BindingAdapter("setSearchIconClickListener", requireAll = false)
fun setSearchLayoutNewSearchIconClickListener(layout: PadSearchLayoutNew, listener: (() -> Unit)?) {
layout.iv.setOnClickListener {
listener?.invoke()
}
}
///////////////////////////////////////////////////////////////////////////
// PadSearchLayoutNew 的绑定适配器
///////////////////////////////////////////////////////////////////////////
@BindingAdapter(
"type",
"hint",
"required",
"icon",
requireAll = false
)
fun setSearchLayoutNewData(
layout: PadSearchLayoutNew,
type: SearchLayoutType?,
hint: String?,
required: Boolean?,
icon: Any?,
) {
type?.let {
layout.type = type
}
required?.let {
layout.required = required
}
hint?.let {
layout.hint = hint
}
icon?.let {
layout.icon = icon
}
}
@BindingAdapter(
"value",
requireAll = false
)
fun setSearchLayoutNewDataValue(
layout: PadSearchLayoutNew,
value: String?,
) {
value?.let {
layout.value = value
}
}
@BindingAdapter(
"enable",
requireAll = false
)
fun setSearchLayoutNewDataEnable(
layout: PadSearchLayoutNew,
enable: Boolean?,
) {
enable?.let {
layout.enable = enable
}
}
@BindingAdapter(
"list",
requireAll = false
)
fun setSearchLayoutNewDataList(
layout: PadSearchLayoutNew,
list: List<KeyValue>?,
) {
list?.let {
layout.list = list
}
}
@BindingAdapter(
"bgDrawable",
requireAll = false
)
fun setSearchLayoutNewDataBackground(
layout: PadSearchLayoutNew,
bgDrawable: Drawable,
) {
layout.ll.setBackgroundDrawable(bgDrawable)
}
@InverseBindingAdapter(attribute = "value", event = "valueAttrChanged")
fun getSearchLayoutNewValue(layout: PadSearchLayoutNew): String {
return layout.value
}
@BindingAdapter("valueAttrChanged", requireAll = false)
fun setSearchLayoutNewValueAttrChanged(layout: PadSearchLayoutNew, listener: InverseBindingListener) {
layout.onChangeListener = listener
}
@BindingAdapter("setOnIconClickListener", requireAll = false)
fun setSearchLayoutNewOnIconClickListener(layout: PadSearchLayoutNew, listener: View.OnClickListener?) {
layout.iv.setOnClickListener(listener)
}
@BindingAdapter("setRefreshCallBack", requireAll = false)
fun setSearchLayoutNewRefreshCallBack(layout: PadSearchLayoutNew, listener: (() -> Unit)?) {
layout.refreshCallBack = listener
}
@BindingAdapter("setSearchListRefresh")
fun setSearchLayoutNewListRefreshCallBack(layout: PadSearchLayoutNew, listener: (() -> Unit)?) {
layout.listRefreshCallBack = listener
}
@BindingAdapter("setTextAllCaps", requireAll = false)
fun setSearchLayoutNewTextAllCaps(layout: PadSearchLayoutNew, textAllCaps: Boolean) {
if (textAllCaps) {
layout.et.filters = arrayOf<InputFilter>(InputFilter.AllCaps())
} else {
layout.et.filters = emptyArray<InputFilter>()
}
}
@BindingAdapter("setInputWaybill", requireAll = false)
fun setSearchLayoutNewInputWaybill(layout: PadSearchLayoutNew, isWaybill: Boolean) {
if (isWaybill) {
EditTextUtils.setMaxLength(layout.et, 11)
EditTextUtils.setInputType(layout.et, InputType.TYPE_CLASS_NUMBER)
layout.et.doOnTextChanged { text, _, _, _ ->
if (text.toString().length == 11) {
layout.refreshCallBack?.invoke()
}
if (text.toString().length in 4..8){
layout.listRefreshCallBack?.invoke()
}
}
}
}

View File

@@ -1,12 +1,9 @@
package com.lukouguoji.module_base.util
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.module_base.bean.DictBean
import com.lukouguoji.module_base.bean.DictListBean
import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.ktx.launchCollect
import com.lukouguoji.module_base.ktx.toMap
import com.lukouguoji.module_base.ktx.toRequestBody
import dev.utils.app.info.KeyValue
import java.util.Collections.emptyList
@@ -179,7 +176,7 @@ object DictUtils {
) {
launchCollect({
NetApply.api
.getSpecialCodeList(flag, ieFlag,parentcode)
.getSpecialCodeList(flag, ieFlag, parentcode)
}) {
onSuccess = {
handleCallBack(it, checkedValue, addAll, callBack)
@@ -404,6 +401,22 @@ object DictUtils {
}
}
/**
* 国际出港通道号列表
*/
fun getGjcChannelList(
callBack: (List<KeyValue>) -> Unit
) {
launchCollect({
NetApply.api
.getDictListByLocation("GJCPASSAGEWAY")
}) {
onSuccess = {
callBack((it.data ?: emptyList()).map { b -> b.toKeyValue() })
}
}
}
/**
* 货物类型
@@ -510,7 +523,7 @@ object DictUtils {
.getDictList("DGRDETAIL")
}) {
onSuccess = {
callBack((it.data ?: emptyList()).map { b -> KeyValue(b.value,b.value) })
callBack((it.data ?: emptyList()).map { b -> KeyValue(b.value, b.value) })
}
}
}
@@ -629,12 +642,14 @@ object DictUtils {
NetApply.api.getUserByRoleId("6")
}) {
onSuccess = {
val jbDrivers = (it.data ?: emptyList()).map { b -> KeyValue(b.username, b.username) }
val jbDrivers =
(it.data ?: emptyList()).map { b -> KeyValue(b.username, b.username) }
launchCollect({
NetApply.api.getUserByRoleId("7")
}) {
onSuccess = {iit ->
val shDrivers = (iit.data ?: emptyList()).map { b -> KeyValue(b.username, b.username) }
onSuccess = { iit ->
val shDrivers =
(iit.data ?: emptyList()).map { b -> KeyValue(b.username, b.username) }
callBack(jbDrivers + shDrivers)
}
}

View File

@@ -2,7 +2,6 @@ package com.lukouguoji.module_base.util
import android.Manifest
import android.graphics.Typeface
import android.util.Log
import com.gainscha.sdk2.ConnectType
import com.gainscha.sdk2.ConnectionListener
import com.gainscha.sdk2.Printer
@@ -18,23 +17,39 @@ import com.gprinter.io.PortManager
import com.gprinter.utils.CallbackListener
import com.gprinter.utils.Command
import com.gprinter.utils.ConnMethod
import com.lukouguoji.module_base.bean.FlatcarBean
import com.lukouguoji.module_base.bean.GjcUldUseBean
import com.lukouguoji.module_base.bean.GncFuBangBean
import com.lukouguoji.module_base.bean.GncShouYunBean
import com.lukouguoji.module_base.http.net.NetApply
import com.lukouguoji.module_base.ktx.launchCollect
import com.lukouguoji.module_base.ktx.launchLoadingCollect
import com.lukouguoji.module_base.ktx.loge
import com.lukouguoji.module_base.ktx.permission
import com.lukouguoji.module_base.ktx.showToast
import com.lukouguoji.module_base.model.LoadingModel
import dev.DevUtils
import dev.utils.app.SizeUtils
import okio.internal.commonToUtf8String
object PrinterUtils {
/**
* 打印单元格数据
*/
private data class CellData(
val title: String, // 中文标签(大字)
val subtitle: String, // 英文标签(小字)
val value: String // 值(大字,垂直居中)
)
/**
* 打印表格行数据
*/
private data class GridRow(
val left: CellData? = null,
val right: CellData? = null,
val merged: CellData? = null // 合并单元格数据
)
private val loading by lazy {
LoadingModel()
}
@@ -133,7 +148,8 @@ object PrinterUtils {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
) {
finder.searchPrinters(ConnectType.BLUETOOTH,
finder.searchPrinters(
ConnectType.BLUETOOTH,
object : PrinterFinder.SimpleSearchPrinterResultListener() {
override fun onSearchBluetoothPrinter(device: BluetoothPrinterDevice?) {
if (device == null) return
@@ -218,28 +234,28 @@ object PrinterUtils {
var fDestinationCn = ""
launchCollect({
NetApply.api.getAirportByCode(bean.dest1)
}){
}) {
onSuccess = {
destinationCn = it.data?.nameCn ?: ""
rowItem.add(listOf("日期", "航班号", "目的站"))
rowItem.add(listOf( bean.fdate, bean.fno, bean.dest1 + destinationCn ))
rowItem.add(listOf("斗号", "计重", "仓管"))
rowItem.add(listOf( bean.location, bean.username, ""))
rowItem.add(listOf("装机仓位", "机位", "特车司机"))
rowItem.add(listOf( "", "", ""))
rowItem.add(listOf("监装监卸", "中转信息","备注"))
rowItem.add(listOf(bean.fdate, bean.fno, bean.dest1 + destinationCn))
rowItem.add(listOf("斗号", "计重", "仓管"))
rowItem.add(listOf(bean.location, bean.username, ""))
rowItem.add(listOf("装机仓位", "机位", "特车司机"))
rowItem.add(listOf("", "", ""))
rowItem.add(listOf("监装监卸", "中转信息", "备注"))
if (bean.dest2 != "") {
launchCollect({
NetApply.api.getAirportByCode(bean.dest2)
}){
}) {
onSuccess = { it2 ->
fDestinationCn = it2.data?.nameCn ?: ""
rowItem.add(listOf( "", bean.dest2 + fDestinationCn, ""))
rowItem.add(listOf("", bean.dest2 + fDestinationCn, ""))
printCommonGridNew(rowItem)
}
}
} else {
rowItem.add(listOf( "", "", ""))
rowItem.add(listOf("", "", ""))
printCommonGridNew(rowItem)
}
}
@@ -259,35 +275,35 @@ object PrinterUtils {
var fDestinationCn = ""
launchCollect({
NetApply.api.getAirportByCode(bean.fdest)
}){
}) {
onSuccess = {
fDestinationCn = it.data?.nameCn ?: ""
if (bean.dest != "") {
launchCollect({
NetApply.api.getAirportByCode(bean.dest)
}){
}) {
onSuccess = { it2 ->
destinationCn = it2.data?.nameCn ?: ""
rowItem.add(listOf("日期", "航班号", "目的站"))
rowItem.add(listOf( bean.fdate, bean.fno, bean.fdest + fDestinationCn ))
rowItem.add(listOf("斗号", "计重", "仓管"))
rowItem.add(listOf( bean.location, bean.username, ""))
rowItem.add(listOf("装机仓位", "机位", "特车司机"))
rowItem.add(listOf( "", "", ""))
rowItem.add(listOf("监装监卸", "中转信息","备注"))
rowItem.add(listOf( "", bean.dest + destinationCn, ""))
rowItem.add(listOf(bean.fdate, bean.fno, bean.fdest + fDestinationCn))
rowItem.add(listOf("斗号", "计重", "仓管"))
rowItem.add(listOf(bean.location, bean.username, ""))
rowItem.add(listOf("装机仓位", "机位", "特车司机"))
rowItem.add(listOf("", "", ""))
rowItem.add(listOf("监装监卸", "中转信息", "备注"))
rowItem.add(listOf("", bean.dest + destinationCn, ""))
printCommonGridNew(rowItem)
}
}
} else {
rowItem.add(listOf("日期", "航班号", "目的站"))
rowItem.add(listOf( bean.fdate, bean.fno, bean.fdest + fDestinationCn ))
rowItem.add(listOf("斗号", "计重", "仓管"))
rowItem.add(listOf( bean.location, bean.username, ""))
rowItem.add(listOf("装机仓位", "机位", "特车司机"))
rowItem.add(listOf( "", "", ""))
rowItem.add(listOf("监装监卸", "中转信息","备注"))
rowItem.add(listOf( "", "", ""))
rowItem.add(listOf(bean.fdate, bean.fno, bean.fdest + fDestinationCn))
rowItem.add(listOf("斗号", "计重", "仓管"))
rowItem.add(listOf(bean.location, bean.username, ""))
rowItem.add(listOf("装机仓位", "机位", "特车司机"))
rowItem.add(listOf("", "", ""))
rowItem.add(listOf("监装监卸", "中转信息", "备注"))
rowItem.add(listOf("", "", ""))
printCommonGridNew(rowItem)
}
@@ -295,6 +311,43 @@ object PrinterUtils {
}
}
/**
* 打印国际出港板箱过磅挂签2列布局支持合并单元格
*/
fun printGjcBoxWeighing(bean: GjcUldUseBean) {
val rows = arrayListOf<GridRow>()
// 第1行日期 | 航班号
rows.add(GridRow(
left = CellData("日期:", "DATA:", bean.fdate),
right = CellData("航班号:", "FLIGHT NO.:", bean.fno)
))
// 第2行目的站 | 板型
rows.add(GridRow(
left = CellData("目的站:", "DESTINATION:", bean.fdest),
right = CellData("板型:", "PALLET TYPE:", bean.boardType)
))
// 第3行集装器编号合并两列
rows.add(GridRow(
merged = CellData("集装器编号:", "ULD NO.:", bean.uld)
))
// 第4行件数 | 重量
rows.add(GridRow(
left = CellData("件数:", "PIECES:", bean.pieces.ifEmpty { "0" }),
right = CellData("重量:", "GROSS WEIGHT:", bean.totalWeight.toInt().toString())
))
// 第5行备注合并两列
rows.add(GridRow(
merged = CellData("备注:", "NOTES:", bean.remark)
))
printMergedGrid(rows)
}
private fun printCommonGrid(rowItem: ArrayList<List<String>>) {
val gridStartX = 80
val gridStartY = 300
@@ -370,10 +423,10 @@ object PrinterUtils {
if (index % 2 == 0) {
padding = 15
}
if (str.length == 2){
if (str.length == 2) {
padding += 60
}
if (str.length == 3){
if (str.length == 3) {
padding += 30
}
if (str.length == 7) {
@@ -391,6 +444,127 @@ object PrinterUtils {
portManager?.writeDataImmediately(bytes)
}
/**
* 打印支持合并单元格的表格
*/
private fun printMergedGrid(rows: ArrayList<GridRow>) {
val gridStartX = 30
val gridStartY = 250
val columnWidth = 570
val rowHeight = 240
val gridWidth = columnWidth * 2
val gridHeight = rowHeight * rows.size
val bytes = Tspl().apply {
addSize(100, 100)
addGap(3)
addCls()
addTextByBitmap(80, 80, 0, 130, "扬州泰州机场", Typeface.DEFAULT)
// 绘制表格横线
for (i in 0..rows.size) {
addBar(gridStartX, gridStartY + (i * rowHeight), gridWidth, 2)
}
// 绘制表格竖线(需要根据是否合并单元格决定)
rows.forEachIndexed { rowIndex, row ->
val yTop = gridStartY + (rowIndex * rowHeight)
// 左边线(总是绘制)
addBar(gridStartX, yTop, 2, rowHeight)
// 中间分隔线(非合并单元格才绘制)
if (row.merged == null) {
addBar(gridStartX + columnWidth, yTop, 2, rowHeight)
}
// 右边线(总是绘制)
if (rowIndex == rows.size - 1) {
addBar(gridStartX + gridWidth, yTop, 2, rowHeight)
}
}
// 填充单元格内容
rows.forEachIndexed { rowIndex, row ->
val yBase = gridStartY + (rowIndex * rowHeight)
if (row.merged != null) {
// 合并单元格
renderCell(
cell = row.merged,
x = gridStartX + 30,
y = yBase,
cellWidth = gridWidth,
cellHeight = rowHeight
)
} else {
// 左列单元格
row.left?.let { cell ->
renderCell(
cell = cell,
x = gridStartX + 30,
y = yBase,
cellWidth = columnWidth,
cellHeight = rowHeight
)
}
// 右列单元格
row.right?.let { cell ->
renderCell(
cell = cell,
x = gridStartX + columnWidth + 30,
y = yBase,
cellWidth = columnWidth,
cellHeight = rowHeight
)
}
}
}
addPrint(1)
}.bytes
portManager?.writeDataImmediately(bytes)
}
/**
* 渲染单元格内容
* @param cell 单元格数据
* @param x 起始X坐标
* @param y 起始Y坐标
* @param cellWidth 单元格宽度(用于计算右侧对齐)
* @param cellHeight 单元格高度(用于计算垂直居中)
*/
private fun Tspl.renderCell(
cell: CellData,
x: Int,
y: Int,
cellWidth: Int,
cellHeight: Int
) {
// 左上角:中文标签和英文标签
val titleY = y + 30 // 中文标签Y位置
val subtitleY = y + 80 // 英文标签Y位置中文下方
// 中文标签(大字,左上)
addText(x, titleY, Tspl.FONT_TSS24, 0, 3, 3, cell.title)
// 英文标签(小字,左上)
addText(x, subtitleY, Tspl.FONT_TSS24, 0, 2, 2, cell.subtitle)
// 右侧:值(大字,垂直居中)
// 计算垂直居中的Y坐标y + (cellHeight / 2) - (文字高度 / 2)
// 假设size=3的文字高度约为80像素
val valueY = y + (cellHeight / 2) - 40 // 垂直居中
// 计算右侧X坐标中文标签宽度 + 间距
// 假设"目的站:"宽度约为180像素"DESTINATION:"宽度约为240像素
val valueX = x + 280 // 右侧偏移量
// 值(大字,垂直居中,右侧)
addText(valueX, valueY, Tspl.FONT_TSS24, 0, 3, 3, cell.value)
}
fun printTest() {
getConnectedPrinters().forEach {
val bytes = v(Instruction.TSC.toString(), null).bytes

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#1C8CF5" />
<corners android:radius="4dp" />
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/color_f2" />
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorPrimary" />
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#FFC107" />
</shape>

View File

@@ -2,6 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_data_layout_s" android:state_enabled="true" />
<item android:drawable="@drawable/bg_data_layout_n" android:state_enabled="false" />
<item android:drawable="@drawable/bg_data_layout_readonly" android:state_enabled="false" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_data_layout_new_s" android:state_enabled="true" />
<item android:drawable="@drawable/bg_data_layout_new_readonly" android:state_enabled="false" />
</selector>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#f2f2f2"/>
<corners android:radius="5dp"/>
<stroke android:width="1px" android:color="#e4e4e4"/>
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white"/>
<stroke android:width="1dp" android:color="#e4e4e4"/>
<corners android:radius="5dp"/>
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#F5F5F5"/>
<corners android:radius="4dp"/>
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/backgroud_gray" />
<corners android:radius="10dp" />
</shape>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E8F5E9" />
<corners android:radius="4dp" />
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#4CAF50" />
<corners android:radius="4dp" />
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_red_radius_4" android:state_enabled="true" />
<item android:drawable="@drawable/bg_gray_radius_4" android:state_enabled="false" />
</selector>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#dc7771" />
<corners android:radius="4dp" />
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E0E0E0" />
<corners android:radius="8dp" />
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_search_layout_s_new" android:state_enabled="true" />
<item android:drawable="@drawable/bg_search_layout_n_new" android:state_enabled="false" />
</selector>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/color_f2" />
<corners android:radius="20dp" />
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:width="8.7dp"
android:height="6dp"
android:drawable="@drawable/img_sp_down"
android:gravity="center_vertical|right"
android:right="10dp" />
</layer-list>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
<corners
android:bottomLeftRadius="8dp"
android:bottomRightRadius="8dp" />
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
<corners
android:topLeftRadius="8dp"
android:topRightRadius="8dp" />
</shape>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="#999999"
android:pathData="M8,4L8,16L12,10L8,4Z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="#4CAF50"
android:pathData="M7.5,14.5L3,10L4.4,8.6L7.5,11.7L15.6,3.6L17,5L7.5,14.5Z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#1C8CF5"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</vector>

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_container"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="4dp"
android:focusableInTouchMode="false"
android:focusable="false"
android:clickable="false"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标题:"
android:textColor="@color/text_gray"
tools:text="标题" />
<LinearLayout
android:id="@+id/ll"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:background="@drawable/bg_data_layout_new"
android:gravity="center_vertical">
<EditText
android:id="@+id/et"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:paddingStart="10dp"
android:textColor="@color/text_normal"
android:textColorHint="@color/text_gray"
android:textSize="14sp" />
<Spinner
android:id="@+id/spinner"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/bg_spinner_pda_new"
android:overlapAnchor="false"
android:spinnerMode="dropdown" />
<TextView
android:id="@+id/tv"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:paddingStart="10dp"
android:textColor="@color/text_normal"
android:textColorHint="@color/text_gray_l"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_spinner"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/bg_spinner_pda_new"
android:gravity="center_vertical"
android:paddingStart="10dp"
android:textColor="@color/text_normal"
android:textColorHint="@color/text_gray"
android:textSize="14sp" />
<ImageView
android:id="@+id/iv"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/scan_code"
android:visibility="gone" />
</LinearLayout>
<TextView
android:id="@+id/tv_m"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="*"
android:textColor="@color/red"
android:textSize="16sp"
android:visibility="invisible" />
</LinearLayout>

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="5dp"
android:background="@drawable/bg_search_layout_new"
android:gravity="center_vertical"
android:orientation="horizontal">
<EditText
android:id="@+id/et"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:paddingStart="10dp"
android:singleLine="true"
android:textColor="@color/text_normal"
android:textColorHint="@color/text_gray_l"
android:textSize="14sp"
tools:text="edittext" />
<Spinner
android:id="@+id/spinner"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/bg_spinner_pda_new"
android:overlapAnchor="false"
android:spinnerMode="dropdown" />
<TextView
android:id="@+id/tv"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:paddingStart="10dp"
android:textColor="@color/text_normal"
android:textColorHint="@color/text_gray_l"
android:textSize="14sp"
tools:text="textView" />
<ImageView
android:id="@+id/iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="10dp"
android:src="@mipmap/scan_code"
android:visibility="gone" />
<TextView
android:id="@+id/tv_m"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="*"
android:textColor="@color/red"
android:textSize="16sp"
android:visibility="gone" />
</LinearLayout>

View File

@@ -14,6 +14,7 @@
android:gravity="center">
<ImageView
android:id="@+id/tool_iv_back"
android:layout_width="15dp"
android:layout_height="15dp"
android:src="@mipmap/left_icon"

View File

@@ -32,8 +32,13 @@
<color name="text_pink">#EA6BE1</color>
<color name="text_red">#d9001b</color>
<color name="text_green">#4CAF50</color>
<color name="text_orange">#FF9800</color>
<color name="color_bottom_layout">#5c6890</color>
<color name="color_f2">#F2F2F2</color>
<color name="line">#EEEEEE</color>
<color name="transparent">#00000000</color>
<color name="bottom_tool_tips_text_color">#797979</color>
</resources>

View File

@@ -0,0 +1,69 @@
package com.lukouguoji.gjc.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcAssembleAllocateBinding
import com.lukouguoji.gjc.viewModel.GjcAssembleAllocateViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.getLifecycleOwner
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际出港组装分配列表页
*/
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_ASSEMBLE_ALLOCATE)
class GjcAssembleAllocateActivity :
BaseBindingActivity<ActivityGjcAssembleAllocateBinding, GjcAssembleAllocateViewModel>() {
override fun layoutId() = R.layout.activity_gjc_assemble_allocate
override fun viewModelClass() = GjcAssembleAllocateViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("组装分配")
binding.viewModel = viewModel
// 绑定分页逻辑
viewModel.pageModel
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner())
// 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH)
.observe(this) {
viewModel.refresh()
}
// 监听checkbox状态变化事件
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED)
.observe(this) {
viewModel.onItemCheckChanged()
}
// 监听全选状态变化,更新图标
viewModel.isAllChecked.observe(this) { isAllChecked ->
// 通过alpha值表示全选状态全选时alpha=1.0未全选时alpha=0.5
binding.checkIcon.alpha = if (isAllChecked) 1.0f else 0.5f
}
// 初始化:获取分配人列表
viewModel.getAssembleCompanyList()
// 初始加载
viewModel.refresh()
}
companion object {
@JvmStatic
fun start(context: Context) {
val starter = Intent(context, GjcAssembleAllocateActivity::class.java)
context.startActivity(starter)
}
}
}

View File

@@ -7,6 +7,7 @@ import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcBoxWeighingAddBinding
import com.lukouguoji.gjc.viewModel.GjcBoxWeighingAddViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
/**
* 国际出港板箱过磅添加页面
@@ -22,6 +23,11 @@ class GjcBoxWeighingAddActivity :
setBackArrow("板箱过磅")
binding.viewModel = viewModel
viewModel.initOnCreated(this)
// 为架子车号、ULD编码、IMP代码添加大写字母和数字的输入限制
binding.carIdInput.et.setUpperCaseAlphanumericFilter()
binding.uldNoInput.et.setUpperCaseAlphanumericFilter()
binding.impCodeInput.et.setUpperCaseAlphanumericFilter()
}
companion object {

View File

@@ -8,6 +8,7 @@ import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcHandoverBinding
import com.lukouguoji.gjc.viewModel.GjcHandoverViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants
/**
@@ -25,12 +26,16 @@ class GjcHandoverActivity :
setBackArrow("货物交接单")
binding.viewModel = viewModel
// 初始化数据
viewModel.initOnCreated(intent)
}
companion object {
@JvmStatic
fun start(context: Context) {
fun start(context: Context, maWbId: Long) {
val starter = Intent(context, GjcHandoverActivity::class.java)
.putExtra(Constant.Key.MAWB_ID, maWbId)
context.startActivity(starter)
}
}

View File

@@ -38,7 +38,8 @@ class GjcInspectionDetailsActivity :
// 右侧文档按钮点击事件
findViewById<android.widget.ImageView>(R.id.ivDocument).setOnClickListener {
GjcHandoverActivity.start(this)
val maWbId = viewModel.dataBean.value?.maWbId ?: 0L
GjcHandoverActivity.start(this, maWbId)
}
binding.viewModel = viewModel

View File

@@ -0,0 +1,56 @@
package com.lukouguoji.gjc.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcQueryBinding
import com.lukouguoji.gjc.viewModel.GjcQueryViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.getLifecycleOwner
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际出港查询列表页
*/
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_QUERY_LIST)
class GjcQueryActivity :
BaseBindingActivity<ActivityGjcQueryBinding, GjcQueryViewModel>() {
override fun layoutId() = R.layout.activity_gjc_query
override fun viewModelClass() = GjcQueryViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港查询")
binding.viewModel = viewModel
// 绑定分页逻辑
viewModel.pageModel
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner())
// 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh()
}
// 初始化代理列表
viewModel.initAgentList()
// 初始加载
viewModel.refresh()
}
companion object {
@JvmStatic
fun start(context: Context) {
val starter = Intent(context, GjcQueryActivity::class.java)
context.startActivity(starter)
}
}
}

View File

@@ -0,0 +1,58 @@
package com.lukouguoji.gjc.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcQueryDetailsBinding
import com.lukouguoji.gjc.viewModel.GjcQueryDetailsViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.base.CustomVP2Adapter
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际出港查询详情页面
*/
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_QUERY_DETAILS)
class GjcQueryDetailsActivity :
BaseBindingActivity<ActivityGjcQueryDetailsBinding, GjcQueryDetailsViewModel>() {
override fun layoutId() = R.layout.activity_gjc_query_details
override fun viewModelClass() = GjcQueryDetailsViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港查询详情")
binding.viewModel = viewModel
// 初始化ViewModel(传入maWbId)
viewModel.initOnCreated(intent)
// 配置ViewPager2
binding.vp.adapter = CustomVP2Adapter(
viewModel.fragmentList,
supportFragmentManager,
lifecycle
)
binding.vp.isUserInputEnabled = false // 禁用滑动
binding.vp.offscreenPageLimit = 3 // 预加载3个Fragment
// 监听Tab索引变化,切换Fragment
viewModel.currentTab.observe(this) {
binding.vp.setCurrentItem(it, false) // false:无动画
}
// 加载详情数据
viewModel.loadDetails()
}
companion object {
@JvmStatic
fun start(context: Context, maWbId: Long?) {
val starter = Intent(context, GjcQueryDetailsActivity::class.java)
.putExtra(Constant.Key.ID, maWbId?.toString() ?: "")
context.startActivity(starter)
}
}
}

View File

@@ -0,0 +1,43 @@
package com.lukouguoji.gjc.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.google.gson.Gson
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcQueryEditBinding
import com.lukouguoji.gjc.viewModel.GjcQueryEditViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.bean.GjcMaWb
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际出港运单修改页
*/
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_QUERY_EDIT)
class GjcQueryEditActivity :
BaseBindingActivity<ActivityGjcQueryEditBinding, GjcQueryEditViewModel>() {
override fun layoutId() = R.layout.activity_gjc_query_edit
override fun viewModelClass() = GjcQueryEditViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("国际出港运单修改")
binding.viewModel = viewModel
// 初始化数据
viewModel.initOnCreated(intent)
}
companion object {
@JvmStatic
fun start(context: Context, bean: GjcMaWb) {
val starter = Intent(context, GjcQueryEditActivity::class.java)
.putExtra(Constant.Key.DATA, Gson().toJson(bean))
context.startActivity(starter)
}
}
}

View File

@@ -30,7 +30,8 @@ import com.scwang.smart.refresh.layout.api.RefreshLayout
import java.text.SimpleDateFormat
import java.util.*
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_QUERY_LIST)
@Deprecated(message = "废弃的国际出港查询页面由GjcQueryActivity 接管")
//@Route(path = ARouterConstants.ACTIVITY_URL_GJC_QUERY_LIST)
class GjcQueryListActivity : BaseActivity(), View.OnClickListener {
private val currentTitleName = "国际出港查询"

View File

@@ -30,6 +30,12 @@ class GjcWeighingListActivity :
binding.viewModel = viewModel
// 初始化代理人列表从API获取
viewModel.initAgentList()
// 初始化特码列表从API获取
viewModel.initSpecialCodeList()
// 绑定分页逻辑
viewModel.pageModel
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner())

View File

@@ -0,0 +1,81 @@
package com.lukouguoji.gjc.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcWeighingRecordDetailsBinding
import com.lukouguoji.gjc.holder.GjcCheckInRecordViewHolder
import com.lukouguoji.gjc.viewModel.GjcWeighingRecordDetailsViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.base.CommonAdapter
import com.lukouguoji.module_base.bean.GjcWeighingRecordBean
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际出港计重明细页
*/
@Route(path = ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_RECORD_DETAILS)
class GjcWeighingRecordDetailsActivity :
BaseBindingActivity<ActivityGjcWeighingRecordDetailsBinding, GjcWeighingRecordDetailsViewModel>() {
private lateinit var adapter: CommonAdapter
// 当前编辑模式供ViewHolder获取
var currentEditMode = false
override fun layoutId() = R.layout.activity_gjc_weighing_record_details
override fun viewModelClass() = GjcWeighingRecordDetailsViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("计重明细")
binding.viewModel = viewModel
// 初始化RecyclerView
val layoutManager = LinearLayoutManager(this)
binding.recyclerView.layoutManager = layoutManager
// 添加分割线
val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
binding.recyclerView.addItemDecoration(divider)
adapter = CommonAdapter(
this,
R.layout.item_gjc_check_in_record,
GjcCheckInRecordViewHolder::class.java
)
binding.recyclerView.adapter = adapter
// 监听数据变化更新RecyclerView
viewModel.recordList.observe(this) { records ->
adapter.refresh(records)
}
// 监听编辑模式变化,更新当前编辑模式并刷新列表
viewModel.isEditMode.observe(this) { isEditMode ->
// 更新当前编辑模式状态
currentEditMode = isEditMode
// 刷新所有可见的 ViewHolder
adapter.notifyDataSetChanged()
}
// 初始化数据
viewModel.initOnCreated(intent)
}
companion object {
@JvmStatic
fun start(context: Context, bean: GjcWeighingRecordBean) {
val starter = Intent(context, GjcWeighingRecordDetailsActivity::class.java)
.putExtra(Constant.Key.BEAN, bean)
context.startActivity(starter)
}
}
}

View File

@@ -30,6 +30,12 @@ class GjcWeighingRecordListActivity :
binding.viewModel = viewModel
// 初始化代理人列表
viewModel.initAgentList()
// 初始化特码列表
viewModel.initSpecialCodeList()
// 绑定分页逻辑
viewModel.pageModel
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner())

View File

@@ -1,8 +1,13 @@
package com.lukouguoji.gjc.activity
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityGjcWeighingStartBinding
@@ -18,6 +23,8 @@ import com.lukouguoji.module_base.router.ARouterConstants
class GjcWeighingStartActivity :
BaseBindingActivity<ActivityGjcWeighingStartBinding, GjcWeighingStartViewModel>() {
private var isExpanded = false
override fun layoutId() = R.layout.activity_gjc_weighing_start
override fun viewModelClass() = GjcWeighingStartViewModel::class.java
@@ -26,16 +33,163 @@ class GjcWeighingStartActivity :
setBackArrow("开始计重")
binding.viewModel = viewModel
viewModel.initOnCreated(this, intent)
setupFloatButtons()
}
private fun setupFloatButtons() {
// 主按钮点击事件
binding.mainFloatButton.setOnClickListener {
if (!isExpanded) {
expandButtons()
}
}
// 遮罩层点击事件
binding.maskView.setOnClickListener {
collapseButtons()
}
// 子按钮1点击事件 - 跳转到出港运抵页面
binding.subButton1.setOnClickListener {
com.alibaba.android.arouter.launcher.ARouter.getInstance()
.build(ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE)
.navigation()
collapseButtons()
}
// 子按钮2点击事件 - 跳转到计重记录
binding.subButton2.setOnClickListener {
com.alibaba.android.arouter.launcher.ARouter.getInstance()
.build(ARouterConstants.ACTIVITY_URL_GJC_WEIGHING_RECORD_LIST)
.navigation()
collapseButtons()
}
}
/**
* 展开子按钮
*/
private fun expandButtons() {
isExpanded = true
// 显示遮罩层
binding.maskView.visibility = View.VISIBLE
binding.maskView.alpha = 0f
binding.maskView.animate().alpha(0.3f).setDuration(200).start()
// 显示子按钮
binding.subButton1.visibility = View.VISIBLE
binding.subButton2.visibility = View.VISIBLE
// 子按钮1动画向左上方移动: 左移15dp, 上移55dp
val sub1TransX = ObjectAnimator.ofFloat(binding.subButton1, "translationX", 0f, -5f)
val sub1TransY = ObjectAnimator.ofFloat(binding.subButton1, "translationY", 0f, -25f)
val sub1Alpha = ObjectAnimator.ofFloat(binding.subButton1, "alpha", 0f, 1f)
val sub1Scale = ObjectAnimator.ofFloat(binding.subButton1, "scaleX", 0f, 1f)
val sub1ScaleY = ObjectAnimator.ofFloat(binding.subButton1, "scaleY", 0f, 1f)
val animSet1 = AnimatorSet()
animSet1.playTogether(sub1TransX, sub1TransY, sub1Alpha, sub1Scale, sub1ScaleY)
animSet1.duration = 200
// 子按钮2动画向左下方移动: 左移55dp, 下移15dp
val sub2TransX = ObjectAnimator.ofFloat(binding.subButton2, "translationX", 0f, -25f)
val sub2TransY = ObjectAnimator.ofFloat(binding.subButton2, "translationY", 0f, -5f)
val sub2Alpha = ObjectAnimator.ofFloat(binding.subButton2, "alpha", 0f, 1f)
val sub2Scale = ObjectAnimator.ofFloat(binding.subButton2, "scaleX", 0f, 1f)
val sub2ScaleY = ObjectAnimator.ofFloat(binding.subButton2, "scaleY", 0f, 1f)
val animSet2 = AnimatorSet()
animSet2.playTogether(sub2TransX, sub2TransY, sub2Alpha, sub2Scale, sub2ScaleY)
animSet2.duration = 200
animSet2.startDelay = 50
// 主按钮隐藏动画
binding.mainFloatButton.animate()
.scaleX(0f)
.scaleY(0f)
.alpha(0f)
.setDuration(150)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
binding.mainFloatButton.visibility = View.GONE
}
})
.start()
animSet1.start()
animSet2.start()
}
/**
* 收起子按钮
*/
private fun collapseButtons() {
isExpanded = false
// 隐藏遮罩层
binding.maskView.animate().alpha(0f).setDuration(200).withEndAction {
binding.maskView.visibility = View.GONE
}.start()
// 子按钮1动画回到原位
val sub1TransX = ObjectAnimator.ofFloat(binding.subButton1, "translationX", binding.subButton1.translationX, 0f)
val sub1TransY = ObjectAnimator.ofFloat(binding.subButton1, "translationY", binding.subButton1.translationY, 0f)
val sub1Alpha = ObjectAnimator.ofFloat(binding.subButton1, "alpha", 1f, 0f)
val sub1Scale = ObjectAnimator.ofFloat(binding.subButton1, "scaleX", 1f, 0f)
val sub1ScaleY = ObjectAnimator.ofFloat(binding.subButton1, "scaleY", 1f, 0f)
val animSet1 = AnimatorSet()
animSet1.playTogether(sub1TransX, sub1TransY, sub1Alpha, sub1Scale, sub1ScaleY)
animSet1.duration = 200
// 子按钮2动画回到原位
val sub2TransX = ObjectAnimator.ofFloat(binding.subButton2, "translationX", binding.subButton2.translationX, 0f)
val sub2TransY = ObjectAnimator.ofFloat(binding.subButton2, "translationY", binding.subButton2.translationY, 0f)
val sub2Alpha = ObjectAnimator.ofFloat(binding.subButton2, "alpha", 1f, 0f)
val sub2Scale = ObjectAnimator.ofFloat(binding.subButton2, "scaleX", 1f, 0f)
val sub2ScaleY = ObjectAnimator.ofFloat(binding.subButton2, "scaleY", 1f, 0f)
val animSet2 = AnimatorSet()
animSet2.playTogether(sub2TransX, sub2TransY, sub2Alpha, sub2Scale, sub2ScaleY)
animSet2.duration = 200
animSet1.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
binding.subButton1.visibility = View.GONE
}
})
animSet2.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
binding.subButton2.visibility = View.GONE
}
})
// 主按钮显示动画
binding.mainFloatButton.visibility = View.VISIBLE
binding.mainFloatButton.animate()
.scaleX(1f)
.scaleY(1f)
.alpha(1f)
.setDuration(200)
.setStartDelay(100)
.setListener(null)
.start()
animSet1.start()
animSet2.start()
}
companion object {
/**
* 启动开始计重页面
* @param context 上下文
* @param maWbId 运单ID
* @param maWbId 运单ID默认为0新增模式
*/
@JvmStatic
fun startForAdd(context: Context, maWbId: Long) {
fun startForAdd(context: Context, maWbId: Long = 0) {
val starter = Intent(context, GjcWeighingStartActivity::class.java)
.putExtra(Constant.Key.MAWB_ID, maWbId)
context.startActivity(starter)

View File

@@ -0,0 +1,69 @@
package com.lukouguoji.gjc.activity
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityIntExpArriveBinding
import com.lukouguoji.gjc.viewModel.IntExpArriveViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.addOnItemClickListener
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际出港-出港运抵页面
*/
@Route(path = ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE)
class IntExpArriveActivity :
BaseBindingActivity<ActivityIntExpArriveBinding, IntExpArriveViewModel>() {
override fun layoutId() = R.layout.activity_int_exp_arrive
override fun viewModelClass() = IntExpArriveViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("出港运抵")
binding.viewModel = viewModel
// 观察全选状态,更新图标透明度
viewModel.isAllChecked.observe(this) { isAllChecked ->
binding.checkIcon.alpha = if (isAllChecked) 1.0f else 0.5f
}
// 绑定分页
viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this)
// 设置item点击监听
binding.rv.addOnItemClickListener(viewModel)
// 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh()
}
// 初始加载数据
viewModel.refresh()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
Constant.RequestCode.WAYBILL -> {
if (resultCode == Activity.RESULT_OK) {
viewModel.waybillNo.value = data?.getStringExtra(Constant.Result.CODED_CONTENT)
viewModel.searchClick()
}
}
Constant.RequestCode.CODE -> {
if (resultCode == Activity.RESULT_OK) {
viewModel.hno.value = data?.getStringExtra(Constant.Result.CODED_CONTENT)
viewModel.searchClick()
}
}
}
}
}

View File

@@ -0,0 +1,70 @@
package com.lukouguoji.gjc.activity
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityIntExpLoadBinding
import com.lukouguoji.gjc.viewModel.IntExpLoadViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.addOnItemClickListener
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际出港-出港装载页面
*/
@Route(path = ARouterConstants.ACTIVITY_URL_INT_EXP_LOAD)
class IntExpLoadActivity :
BaseBindingActivity<ActivityIntExpLoadBinding, IntExpLoadViewModel>() {
override fun layoutId() = R.layout.activity_int_exp_load
override fun viewModelClass() = IntExpLoadViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("出港装载")
binding.viewModel = viewModel
// 观察全选状态,更新图标透明度
viewModel.isAllChecked.observe(this) { isAllChecked ->
binding.checkIcon.alpha = if (isAllChecked) 1.0f else 0.5f
}
// 绑定分页
viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this)
// 设置item点击监听
binding.rv.addOnItemClickListener(viewModel)
// 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh()
}
// 初始加载数据
viewModel.refresh()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
val codedContent = data?.getStringExtra(Constant.Result.CODED_CONTENT) ?: return
when (requestCode) {
// 扫码运单号
Constant.RequestCode.WAYBILL -> {
viewModel.waybillNo.value = codedContent
viewModel.searchClick()
}
// 扫码分单号
Constant.RequestCode.CODE -> {
viewModel.houseWaybillNo.value = codedContent
viewModel.searchClick()
}
}
}
}
}

View File

@@ -0,0 +1,59 @@
package com.lukouguoji.gjc.activity
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityIntExpOutHandoverBinding
import com.lukouguoji.gjc.viewModel.IntExpOutHandoverViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.addOnItemClickListener
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际出港-出库交接页面
*/
@Route(path = ARouterConstants.ACTIVITY_URL_INT_EXP_OUT_HANDOVER)
class IntExpOutHandoverActivity :
BaseBindingActivity<ActivityIntExpOutHandoverBinding, IntExpOutHandoverViewModel>() {
override fun layoutId() = R.layout.activity_int_exp_out_handover
override fun viewModelClass() = IntExpOutHandoverViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("出库交接")
binding.viewModel = viewModel
// 观察全选状态,更新图标透明度
viewModel.isAllChecked.observe(this) { isAllChecked ->
binding.checkIcon.alpha = if (isAllChecked) 1.0f else 0.5f
}
// 绑定分页
viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this)
// 设置item点击监听
binding.rv.addOnItemClickListener(viewModel)
// 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh()
}
// 初始加载数据
viewModel.refresh()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Constant.RequestCode.ULD && resultCode == Activity.RESULT_OK) {
viewModel.uldNo.value = data?.getStringExtra(Constant.Result.CODED_CONTENT)
viewModel.searchClick()
}
}
}

View File

@@ -0,0 +1,68 @@
package com.lukouguoji.gjc.activity
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.ActivityIntExpTallyBinding
import com.lukouguoji.gjc.viewModel.IntExpTallyViewModel
import com.lukouguoji.module_base.base.BaseBindingActivity
import com.lukouguoji.module_base.common.Constant
import com.lukouguoji.module_base.common.ConstantEvent
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe
import com.lukouguoji.module_base.ktx.addOnItemClickListener
import com.lukouguoji.module_base.router.ARouterConstants
/**
* 国际出港-出港理货
*/
@Route(path = ARouterConstants.ACTIVITY_URL_INT_EXP_TALLY)
class IntExpTallyActivity :
BaseBindingActivity<ActivityIntExpTallyBinding, IntExpTallyViewModel>() {
override fun layoutId() = R.layout.activity_int_exp_tally
override fun viewModelClass() = IntExpTallyViewModel::class.java
override fun initOnCreate(savedInstanceState: Bundle?) {
setBackArrow("出港理货")
binding.viewModel = viewModel
// 观察全选状态,动态调整图标透明度
viewModel.isAllChecked.observe(this) { isAllChecked ->
binding.checkIcon.alpha = if (isAllChecked) 1.0f else 0.5f
}
// 绑定分页
viewModel.pageModel.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, this)
// 设置item点击监听
binding.rv.addOnItemClickListener(viewModel)
// 监听刷新事件
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
viewModel.refresh()
}
// 初始加载数据
viewModel.refresh()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
val codedContent = data?.getStringExtra(Constant.Result.CODED_CONTENT) ?: return
when (requestCode) {
Constant.RequestCode.WAYBILL -> { // 运单号
viewModel.waybillNo.value = codedContent
viewModel.searchClick()
}
Constant.RequestCode.CODE -> { // 分单号
viewModel.houseWaybillNo.value = codedContent
viewModel.searchClick()
}
}
}
}
}

View File

@@ -0,0 +1,41 @@
package com.lukouguoji.gjc.dialog
import android.content.Context
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.DialogGjcAssembleAllocateBinding
import com.lukouguoji.module_base.base.BaseDialogModel
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import dev.utils.app.info.KeyValue
/**
* 国际出港组装分配 - 分配人选择对话框
*/
class GjcAssembleAllocateDialogModel(
val flightInfo: String, // 航班信息(用于显示)
val assembleCompanyList: List<KeyValue>, // 分配人列表
private val callback: (GjcAssembleAllocateDialogModel) -> Unit
) : BaseDialogModel<DialogGjcAssembleAllocateBinding>(DIALOG_TYPE_CENTER) {
// 选中的分配人(存储的是 code例如 "ATR"
val allocator = MutableLiveData("")
override fun layoutId(): Int {
return R.layout.dialog_gjc_assemble_allocate
}
override fun onDialogCreated(context: Context) {
binding.model = this
}
/**
* 确认按钮点击
*/
fun onConfirmClick() {
if (allocator.value.verifyNullOrEmpty("请选择分配人")) {
return
}
dismiss()
callback(this)
}
}

View File

@@ -0,0 +1,40 @@
package com.lukouguoji.gjc.dialog
import android.content.Context
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.DialogGjcInspectionRejectBinding
import com.lukouguoji.module_base.base.BaseDialogModel
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
/**
* 国际出港收运检查 - 退回原因对话框
*/
class GjcInspectionRejectDialogModel(
private val callback: (String) -> Unit // 回调传递退回原因
) : BaseDialogModel<DialogGjcInspectionRejectBinding>(DIALOG_TYPE_CENTER) {
// 退回原因
val rejectReason = MutableLiveData("")
override fun layoutId(): Int {
return R.layout.dialog_gjc_inspection_reject
}
override fun onDialogCreated(context: Context) {
binding.model = this
}
/**
* 保存按钮点击
*/
fun onConfirmClick() {
// 验证退回原因
if (rejectReason.value.verifyNullOrEmpty("请输入退回原因")) {
return
}
dismiss()
callback(rejectReason.value!!)
}
}

View File

@@ -0,0 +1,101 @@
package com.lukouguoji.gjc.dialog
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.DialogGjcQueryFilterBinding
import com.lukouguoji.module_base.base.BaseDialogModel
import com.lxj.xpopup.XPopup
import com.lxj.xpopup.enums.PopupPosition
import dev.DevUtils
import dev.utils.app.info.KeyValue
/**
* 国际出港查询筛选抽屉
*/
class GjcQueryFilterDialogModel(
val spCode: MutableLiveData<String>, // 特码
val flightNo: MutableLiveData<String>, // 航班号
val dest: MutableLiveData<String>, // 目的港
val awbType: MutableLiveData<String>, // 运单类型
val businessType: MutableLiveData<String>, // 业务类型
val goodsCn: MutableLiveData<String>, // 品名(中)
private val onConfirm: () -> Unit
) : BaseDialogModel<DialogGjcQueryFilterBinding>(DIALOG_TYPE_DRAWER) {
// 运单类型列表
val awbTypeList = MutableLiveData(
listOf(
KeyValue("全部", ""),
KeyValue("主单", "1"),
KeyValue("分单", "2")
)
)
// 业务类型列表
val businessTypeList = MutableLiveData(
listOf(
KeyValue("全部", ""),
KeyValue("普货", "1"),
KeyValue("特货", "2")
)
)
override fun layoutId() = R.layout.dialog_gjc_query_filter
override fun onBuild(builder: XPopup.Builder) {
super.onBuild(builder)
// 设置从右边弹出
builder.popupPosition(PopupPosition.Right)
// 设置抽屉宽度为 Activity contentView 宽度的1/3横屏长边
val activity = DevUtils.getTopActivity()
val activityWidth = activity.window.decorView.width
builder.maxWidth(activityWidth / 3)
builder.popupWidth(activityWidth / 3)
}
override fun onDialogCreated(context: Context) {
binding.model = this
binding.lifecycleOwner = context as? androidx.lifecycle.LifecycleOwner
val titleColor = Color.parseColor("#666666")
// 设置标题
binding.root.findViewById<TextView>(R.id.title_name)?.text = "筛选条件"
binding.root.findViewById<TextView>(R.id.title_name)?.setTextColor(titleColor)
binding.root.findViewById<TextView>(R.id.tool_tv_back)?.setTextColor(titleColor)
binding.root.findViewById<ImageView>(R.id.tool_iv_back)?.imageTintList = ColorStateList.valueOf(titleColor)
binding.root.findViewById<View>(R.id.toolbar)?.setBackgroundColor(Color.WHITE)
// 返回按钮
binding.root.findViewById<View>(R.id.tool_back)?.setOnClickListener {
dismiss()
}
}
/**
* 重置筛选条件
*/
fun onResetClick() {
spCode.value = ""
flightNo.value = ""
dest.value = ""
awbType.value = ""
businessType.value = ""
goodsCn.value = ""
}
/**
* 确认筛选
*/
fun onConfirmClick() {
dismiss()
onConfirm()
}
}

View File

@@ -0,0 +1,58 @@
package com.lukouguoji.gjc.dialog
import android.content.Context
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.DialogIntExpArriveDeleteBinding
import com.lukouguoji.module_base.base.BaseDialogModel
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import dev.utils.app.info.KeyValue
/**
* 国际出港运抵 - 删除申报对话框
*/
class IntExpArriveDeleteDialogModel(
val changeReasonList: List<KeyValue>, // 变更原因列表
private val callback: (IntExpArriveDeleteDialogModel) -> Unit
) : BaseDialogModel<DialogIntExpArriveDeleteBinding>(DIALOG_TYPE_CENTER) {
// 变更原因代码(存储的是 code
val changeReason = MutableLiveData("")
// 联系人姓名
val contactName = MutableLiveData("")
// 联系人电话
val contactPhone = MutableLiveData("")
override fun layoutId(): Int {
return R.layout.dialog_int_exp_arrive_delete
}
override fun onDialogCreated(context: Context) {
binding.model = this
}
/**
* 确认按钮点击
*/
fun onConfirmClick() {
// 验证变更原因
if (changeReason.value.verifyNullOrEmpty("请选择变更原因")) {
return
}
// 验证联系人姓名
if (contactName.value.verifyNullOrEmpty("请输入联系人姓名")) {
return
}
// 验证联系人电话
if (contactPhone.value.verifyNullOrEmpty("请输入联系人电话")) {
return
}
dismiss()
callback(this)
}
}

View File

@@ -0,0 +1,58 @@
package com.lukouguoji.gjc.dialog
import android.content.Context
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.DialogIntExpLoadDeleteBinding
import com.lukouguoji.module_base.base.BaseDialogModel
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import dev.utils.app.info.KeyValue
/**
* 国际出港装载 - 删除申报对话框
*/
class IntExpLoadDeleteDialogModel(
val changeReasonList: List<KeyValue>, // 变更原因列表
private val callback: (IntExpLoadDeleteDialogModel) -> Unit
) : BaseDialogModel<DialogIntExpLoadDeleteBinding>(DIALOG_TYPE_CENTER) {
// 变更原因代码(存储的是 code
val changeReason = MutableLiveData("")
// 联系人姓名
val contactName = MutableLiveData("")
// 联系人电话
val contactPhone = MutableLiveData("")
override fun layoutId(): Int {
return R.layout.dialog_int_exp_load_delete
}
override fun onDialogCreated(context: Context) {
binding.model = this
}
/**
* 确认按钮点击
*/
fun onConfirmClick() {
// 验证变更原因
if (changeReason.value.verifyNullOrEmpty("请选择变更原因")) {
return
}
// 验证联系人姓名
if (contactName.value.verifyNullOrEmpty("请输入联系人姓名")) {
return
}
// 验证联系人电话
if (contactPhone.value.verifyNullOrEmpty("请输入联系人电话")) {
return
}
dismiss()
callback(this)
}
}

View File

@@ -0,0 +1,58 @@
package com.lukouguoji.gjc.dialog
import android.content.Context
import androidx.lifecycle.MutableLiveData
import com.lukouguoji.gjc.R
import com.lukouguoji.gjc.databinding.DialogIntExpTallyDeleteBinding
import com.lukouguoji.module_base.base.BaseDialogModel
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
import dev.utils.app.info.KeyValue
/**
* 国际出港理货 - 删除申报对话框
*/
class IntExpTallyDeleteDialogModel(
val changeReasonList: List<KeyValue>, // 变更原因列表
private val callback: (IntExpTallyDeleteDialogModel) -> Unit
) : BaseDialogModel<DialogIntExpTallyDeleteBinding>(DIALOG_TYPE_CENTER) {
// 变更原因代码(存储的是 code
val changeReason = MutableLiveData("")
// 联系人姓名
val contactName = MutableLiveData("")
// 联系人电话
val contactPhone = MutableLiveData("")
override fun layoutId(): Int {
return R.layout.dialog_int_exp_tally_delete
}
override fun onDialogCreated(context: Context) {
binding.model = this
}
/**
* 确认按钮点击
*/
fun onConfirmClick() {
// 验证变更原因
if (changeReason.value.verifyNullOrEmpty("请选择变更原因")) {
return
}
// 验证联系人姓名
if (contactName.value.verifyNullOrEmpty("请输入联系人姓名")) {
return
}
// 验证联系人电话
if (contactPhone.value.verifyNullOrEmpty("请输入联系人电话")) {
return
}
dismiss()
callback(this)
}
}

Some files were not shown because too many files have changed in this diff Show More