Compare commits
65 Commits
feature/ph
...
f41d4186ea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f41d4186ea | ||
|
|
d9dc927525 | ||
|
|
d5631930c4 | ||
|
|
5b217ab032 | ||
|
|
f1ff42f2bf | ||
|
|
388d605869 | ||
|
|
131eb031ba | ||
| 871fa4c5ce | |||
| dda0ff873a | |||
| 9e66e892f2 | |||
| 9b820ba8ef | |||
| 74f1280896 | |||
| 2dc86bcae5 | |||
| aebf1b1090 | |||
| 698046c9fb | |||
| 94508f8c40 | |||
| d54e1e429d | |||
| b7a2be0f35 | |||
| 75bfe85f62 | |||
| f39127436a | |||
| 6b02f71e8a | |||
| 50275457a4 | |||
| b14649b228 | |||
| 52d1527121 | |||
| 74cf1346a6 | |||
| 8853405ffc | |||
| ee0606230f | |||
| 07ea8853f3 | |||
| 6d4908da1a | |||
| a1d078db7a | |||
| f6e5b37eaa | |||
| 2f4e723dd7 | |||
| 52584b9cfe | |||
| efe7a1d2a9 | |||
| 85fd66b005 | |||
| de64c8ddb6 | |||
| 2a83c1393e | |||
| 9dddb7c079 | |||
| 2ba70d1a09 | |||
| ea83361102 | |||
| 7a09bbbeab | |||
| 6b2006fdb7 | |||
| b368efa827 | |||
| fcb247c3f5 | |||
| 5edb1bea06 | |||
| 51c5ac6e96 | |||
| ea298ec322 | |||
| b57a3cd133 | |||
| 4e6e654558 | |||
| b65e61595b | |||
| 86f77353c0 | |||
| 39b5cc3689 | |||
| a1b55f656f | |||
| 13c9c6f5dc | |||
| de62b903d4 | |||
| 2d42ce9cc3 | |||
| 861ce8ca1e | |||
| f312ef866c | |||
| 6f27c15d9a | |||
| ded57554fa | |||
| 2f3ae2a814 | |||
| 19d02700b8 | |||
| aa6cc43bfa | |||
| dcb335ecdf | |||
| 567b76014b |
@@ -39,9 +39,6 @@
|
|||||||
"Bash(git -C /Users/kid/Development/Fusion/Projects/aerologic-app stash)",
|
"Bash(git -C /Users/kid/Development/Fusion/Projects/aerologic-app stash)",
|
||||||
"WebFetch(domain:api.apifox.cn)",
|
"WebFetch(domain:api.apifox.cn)",
|
||||||
"Bash(sips:*)",
|
"Bash(sips:*)",
|
||||||
"Bash(vfox list:*)",
|
|
||||||
"Bash(vf list:*)",
|
|
||||||
"Bash(vfox use:*)",
|
|
||||||
"Bash(wait)",
|
"Bash(wait)",
|
||||||
"Bash(while read file)",
|
"Bash(while read file)",
|
||||||
"Bash(do sed -i '' 's/MutableLiveData\\(DateUtils\\\\.getCurrentTime\\(\\)\\\\.formatDate\\(\\)\\)/MutableLiveData<String>\\(DateUtils.getCurrentTime\\(\\).formatDate\\(\\)\\)/g' \"$file\" echo \"Modified: $file\" done)",
|
"Bash(do sed -i '' 's/MutableLiveData\\(DateUtils\\\\.getCurrentTime\\(\\)\\\\.formatDate\\(\\)\\)/MutableLiveData<String>\\(DateUtils.getCurrentTime\\(\\).formatDate\\(\\)\\)/g' \"$file\" echo \"Modified: $file\" done)",
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ curl -s -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \
|
|||||||
- `IntExpMove`:出参无航班字段(缺口 #1)、无操作人姓名(缺口 #2);`opId`/`opdate`/`businessType`
|
- `IntExpMove`:出参无航班字段(缺口 #1)、无操作人姓名(缺口 #2);`opId`/`opdate`/`businessType`
|
||||||
文档有定义但实测恒 null(缺口 #3);`pageQueryTotal` 正常(与 pageQuery 口径一致,`moveState` 过滤生效)
|
文档有定义但实测恒 null(缺口 #3);`pageQueryTotal` 正常(与 pageQuery 口径一致,`moveState` 过滤生效)
|
||||||
- `typeCode/getSpecialCodeByParam`:`flag=1` 国际,`ieFlag` 必须传空串(传 `"E"` 返回空数组)
|
- `typeCode/getSpecialCodeByParam`:`flag=1` 国际,`ieFlag` 必须传空串(传 `"E"` 返回空数组)
|
||||||
|
- `typeCode/searchDateType`:**同一路径两个接口**——`GET ?code=xxx` 是通用字典(`Api.getDictList`),
|
||||||
|
`POST` 无参是「发现时间类型」(`Api.getSearchDateTypeList`,出参 `code` 1~8 + `name` 中英文时机);
|
||||||
|
国际事故签证 `seachDate` 与 Web 端一致存 **name**(历史数据另有 `2026-09-24` 日期值与带 `\r\n` 的 name)
|
||||||
- `eqm/uld`(ULD信息,6 个端点):查询入参仅 `uld`/`status`/`uldSuffix`(uldSuffix 名义是
|
- `eqm/uld`(ULD信息,6 个端点):查询入参仅 `uld`/`status`/`uldSuffix`(uldSuffix 名义是
|
||||||
"uld后两位",实际就是**按所属航司筛选**,配 `DictUtils.getIntCarrierList`);`status` 仅两态
|
"uld后两位",实际就是**按所属航司筛选**,配 `DictUtils.getIntCarrierList`);`status` 仅两态
|
||||||
0 正常/1 故障(三态需求=缺口 #7);无「来源」字段(#8)、无 `checkInDate`/`ifNo` 筛选入参(#9)、
|
0 正常/1 故障(三态需求=缺口 #7);无「来源」字段(#8)、无 `checkInDate`/`ifNo` 筛选入参(#9)、
|
||||||
|
|||||||
@@ -1,49 +1,70 @@
|
|||||||
---
|
---
|
||||||
name: sync-air-cargo
|
name: sync-air-cargo
|
||||||
description: 将 aerologic-app 项目中未提交的变更(已修改、新增、已删除、包括未跟踪的文件)以文件级 cp/rm 方式同步到同级目录下的 air-cargo 项目——两者是同一份源码的两个独立 git 仓库副本,分别推送到不同的远程地址。只做文件复制/删除,不对内容做任何二次编辑或转换。每当用户提到"同步到 air-cargo"、"同步到另一个仓库/项目"、"推送/复制到 cargo 项目"、"两个项目要保持一致",或者在 aerologic-app 里改完代码后问要不要同步/推送到另一个仓库时,应主动使用此 skill,而不是手动逐个 cp 文件。
|
description: 将 aerologic-app 的变更以文件级 cp/rm 方式同步到项目根目录下的 air-cargo/ 仓库并在其本地提交(不 push)——两者是同一份源码的两个独立 git 仓库副本,分别推送到不同的远程地址。同步点记录在 air-cargo 提交信息的 Synced-From trailer 中,自动从上次同步点同步到 main 最新提交(覆盖已提交未 push、已 push 的变更),也支持只同步未提交的工作区变更。只做文件复制/删除,不对内容做任何二次编辑或转换。每当用户提到"同步到 air-cargo"、"同步到另一个仓库/项目"、"推送/复制到 cargo 项目"、"两个项目要保持一致"、"把某几个 commit 同步过去",或者在 aerologic-app 里改完代码后问要不要同步/推送到另一个仓库时,应主动使用此 skill,而不是手动逐个 cp 文件。
|
||||||
---
|
---
|
||||||
|
|
||||||
# 同步变更到 air-cargo
|
# 同步变更到 air-cargo
|
||||||
|
|
||||||
## 背景
|
## 背景
|
||||||
|
|
||||||
`aerologic-app` 和同级目录下的 `air-cargo` 是同一份项目源码的两个独立 git 仓库副本,目录结构完全一致,分别推送到不同的远程仓库地址。日常开发只在 `aerologic-app` 中进行,完成后需要把未提交的变更原样同步一份到 `air-cargo`,再由用户在 `air-cargo` 里自行提交、推送。
|
`aerologic-app` 和 `air-cargo` 是同一份项目源码的两个独立 git 仓库副本,分别推送到不同的远程仓库地址。日常开发只在 `aerologic-app` 中进行,完成后把变更同步到 `air-cargo` 并在本地提交,**push 由用户自己做**。
|
||||||
|
|
||||||
同步的判定完全基于 `git status`:在 `aerologic-app` 里,工作区中存在的文件(无论是修改、新增还是未跟踪)就复制覆盖到 `air-cargo` 对应路径;工作区中已不存在的文件(即被删除的)就在 `air-cargo` 里删除对应路径。这样天然覆盖了修改/新增/删除/未跟踪/重命名(重命名会被当作"删除旧路径 + 新增新路径"处理)等所有情况,不需要逐个状态码去分支判断。
|
- **位置**:`air-cargo` 在 aerologic-app 根目录下(`/Users/tim/Development/Projects/aerologic-app/air-cargo/`),已在 aerologic-app 的 `.gitignore` 中忽略
|
||||||
|
- **分支映射**(`config` 文件):aerologic-app `main` → air-cargo `feature/hefei`。air-cargo 的 `develop` 是 2023 年的旧快照,不能往上同步;当前分支不是 `feature/hefei` 时脚本直接报错
|
||||||
|
- **不参与同步**:`CLAUDE.md` / `CLAUDE.local.md`、`.claude/` 目录、根目录 `.claude.*`(如 `.claude.local.md`)与 `.mcp.json`(Claude Code 的配置/记忆),不复制也不删除。air-cargo 中不应存在任何 Claude / Agent 相关文件,其本地 `.git/info/exclude` 已屏蔽这些文件名,防止误提交
|
||||||
|
- **air-cargo 只是提交通道**:不参与本项目开发,代码扫描、功能实现、查历史时都忽略它(见 CLAUDE.md「air-cargo 目录」一节)
|
||||||
|
|
||||||
**只同步工程项目本身的文件**:`CLAUDE.md` 以及 `.claude/` 目录(包括其下的 `skills/`、`commands/`、`settings.local.json`、`mcp.json` 等)属于 Claude Code 的工具配置/记忆,两个仓库各自独立维护,不属于"项目源码",因此始终排除在同步范围之外,不会被复制也不会被删除。脚本会在输出里单独列出"已忽略,不参与同步"的清单,方便核对。
|
## 同步点:air-cargo 提交信息里的 Synced-From trailer
|
||||||
|
|
||||||
|
每次同步提交的信息只有一句带日期的通用语句加一个 trailer,**不写修改明细**:
|
||||||
|
|
||||||
|
```
|
||||||
|
Update 20261012
|
||||||
|
|
||||||
|
Synced-From: aerologic-app@388d6058696c05cfd5892018f52120880a440524
|
||||||
|
```
|
||||||
|
|
||||||
|
脚本以 air-cargo 当前分支上最近一个 `Synced-From` 作为基线,把 `基线..main` 的变更同步过去。记录跟着 air-cargo 的提交历史走,只有真正提交了才算数。
|
||||||
|
|
||||||
|
- **区间模式(默认)**:基线 = 最近的 `Synced-From`,目标 = aerologic-app `main` 最新提交。**文件内容从目标提交对象中读取**,与 aerologic-app 工作区无关,因此已 commit 未 push、已 push 的变更都用它。提交时写入新的 `Synced-From`
|
||||||
|
- **工作区模式(`--worktree`)**:同步 aerologic-app 未提交的变更。提交信息**不写** `Synced-From`,因为这份内容不对应任何提交,不能推进同步点。之后这些改动在 aerologic-app 提交后,下次区间同步会再覆盖一次(内容相同,无副作用)
|
||||||
|
- **兜底:按内容反推**:找不到 `Synced-From` 时(首次同步,或有人手工提交时丢了 trailer),在 main 的第一父链上找与 air-cargo HEAD 内容差异最少的提交作为候选基线。只用于预览,`apply` 必须显式传 `--base`
|
||||||
|
- **基线校验**:每次预览都会比较 air-cargo HEAD 和基线的内容。正常情况下只剩 air-cargo 自己的 6 个元文件:`.gitignore`(air-cargo 额外忽略 `*.jks`)、`key密码`、`CONTRIBUTING.md`、`备忘录.md`、`国际出港进港权限对照表.xlsx`、`.docs/大写字母数字输入过滤器使用指南.md`。超过 50 个会提示基线或分支可能不对
|
||||||
|
|
||||||
## 使用方法
|
## 使用方法
|
||||||
|
|
||||||
脚本位于 `scripts/sync.sh`,用法:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash scripts/sync.sh [SOURCE_DIR] [TARGET_DIR] [preview|apply]
|
cd /Users/tim/Development/Projects/aerologic-app
|
||||||
|
bash .claude/skills/sync-air-cargo/scripts/sync.sh [preview|apply] [--base <ref>] [--to <ref>] [--worktree] [--label <word>]
|
||||||
```
|
```
|
||||||
|
|
||||||
- `SOURCE_DIR` 默认为当前目录(一般就是 aerologic-app 项目根目录)
|
| 参数 | 说明 |
|
||||||
- `TARGET_DIR` 默认为 `SOURCE_DIR` 同级的 `air-cargo` 目录,一般不需要手动指定
|
|------|------|
|
||||||
- 第三个参数默认 `preview`(只打印清单,不做任何改动),传 `apply` 才会真正执行复制/删除
|
| `preview` / `apply` | 默认 `preview`,只打印计划;`apply` 复制/删除并在 air-cargo 本地提交 |
|
||||||
|
| `--base <ref>` | 指定基线:反推结果经确认后传入,或用来纠正错误的同步点 |
|
||||||
|
| `--to <ref>` | 指定目标提交,默认 `main`(只同步到某个较早的提交时使用) |
|
||||||
|
| `--worktree` | 工作区模式,不能和 `--base`/`--to` 同时用 |
|
||||||
|
| `--label <word>` | 提交信息的通用词,默认 `Update`。用户要求时可换成 `Fixed`、`Optimized` 等 |
|
||||||
|
|
||||||
### 步骤
|
### 步骤
|
||||||
|
|
||||||
1. **先预览**,让用户看到将要发生什么:
|
1. **预览**:先确定模式——用户要同步已提交的代码用默认区间模式;明确说的是"没提交的改动"才加 `--worktree`。然后执行 `sync.sh`(区间模式不加参数)。
|
||||||
```bash
|
- 输出「无需同步」:直接告诉用户,结束
|
||||||
bash /Users/kid/Development/Fusion/Projects/aerologic-app/.claude/skills/sync-air-cargo/scripts/sync.sh
|
- 基线来源是「按内容反推」:把候选基线连同基线校验结果一起拿给用户确认(反推可能取到比预期更早的提交,例如 air-cargo 从未同步过的环境配置提交),确认后加 `--base <sha>` 再预览一次
|
||||||
```
|
2. **用 `AskUserQuestion` 确认**,不要只在文字里问一句。问题描述里要包含:基线 → 目标、区间提交数、待复制/待删除数量(及主要模块分布)、**「⚠ 将覆盖 air-cargo 侧独立修改」清单(逐个列出)**、提交信息。选项至少包含「确认同步并提交」和「取消」。覆盖清单不为空时要特别说明这些文件是 air-cargo 自己改过的(如 `.gitignore` 里的 `*.jks` 忽略),由用户决定。
|
||||||
输出会分别列出"待复制/覆盖"和"待删除"的文件清单。
|
3. **执行**:把预览时的参数原样带上,`preview` 换成 `apply`。脚本会复制/删除文件、精确暂存这些路径并提交,最后输出提交号和变更统计。
|
||||||
|
4. **不要 push**。向用户报告提交号,并说明 air-cargo 比 `origin/feature/hefei` 超前几个提交,push 由用户自己做。
|
||||||
|
|
||||||
2. **用 `AskUserQuestion` 工具向用户确认**,不要只是在文字里问一句让用户打字回复。这是一个会覆盖 `air-cargo` 中已有文件、且删除操作不可逆的操作,必须先把预览清单(待复制、待删除的文件数量和列表)放进问题描述里,让用户能一眼看到影响范围,选项至少包含"确认同步"和"取消"。得到确认后才能继续执行 `apply`(这遵循整体规则:覆盖性/破坏性操作需要用户确认,不要因为 skill 本身能直接跑就跳过确认)。
|
### 脚本的保护措施
|
||||||
|
|
||||||
3. **用户确认后执行同步**:
|
- air-cargo 当前分支不是 `config` 里的 `TARGET_BRANCH` → 报错
|
||||||
```bash
|
- air-cargo 工作区有本次同步范围**以外**的未提交改动 → 报错;范围内的改动(例如上次同步还没提交)允许,重跑会覆盖成相同内容
|
||||||
bash /Users/kid/Development/Fusion/Projects/aerologic-app/.claude/skills/sync-air-cargo/scripts/sync.sh . /Users/kid/Development/Fusion/Projects/air-cargo apply
|
- 基线不是目标的祖先(aerologic-app 被 rebase / force-push 过)→ 报错,用 `--base` 重新指定
|
||||||
```
|
- 区间内只有不参与同步的文件变化 → 仍然提交一个空提交,推进同步点
|
||||||
执行完会输出"复制/更新 N 个文件,删除 M 个文件"的汇总。
|
|
||||||
|
|
||||||
4. **同步之后不要自动 commit/push**。脚本只做文件复制,`air-cargo` 那边的 `git add` / `commit` / `push` 由用户自行决定和操作——两个仓库指向不同的远程地址,提交推送这类会影响远程共享状态的操作应始终由用户主导确认。
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
- 脚本只识别 git 能感知到的变更(`git status` 看得到的),`.gitignore` 忽略的文件(如构建产物 `build/`、`.gradle/`)不会被同步,这也是符合预期的——不应该把本地构建缓存同步过去。
|
- 只同步 git 跟踪的内容,`.gitignore` 忽略的构建产物(`build/`、`.gradle/`)不会同步,`air-cargo/` 本身也不在变更清单里
|
||||||
- 如果 `air-cargo` 里对应路径本身也有未提交的本地修改,会被直接覆盖覆盖掉,预览阶段要留意这一点并提醒用户。
|
- 不要在 air-cargo 里手动提交同步内容。如果确实手动提交了,trailer 格式必须完全一致(`Synced-From: aerologic-app@<完整 sha>`),否则下次只能走反推
|
||||||
|
- 首个同步点:2026-10-12,air-cargo `3dd792f9`,对应 aerologic-app `388d605`(基线选 `871fa4c`)。跳过的 `dda0ff8`(vfox→mise)随后在 air-cargo 单独提交 `3c76385b` 补上:删除 `.tool-versions` / `.vfox.toml` / `.vfox/`,新增与 aerologic-app 相同的 `mise.toml`。这个提交不带 trailer,同步点不变
|
||||||
|
- 两个仓库都用 mise 管理 JDK(`mise.toml`:`java = "17"`)。air-cargo 位于 aerologic-app 之下,mise 会逐级读取配置,所以 air-cargo 里不能再出现 `.tool-versions`,否则会覆盖成未安装的旧版本
|
||||||
|
|||||||
8
.claude/skills/sync-air-cargo/config
Normal file
8
.claude/skills/sync-air-cargo/config
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# sync-air-cargo 配置,由 scripts/sync.sh 以 shell 变量形式读入
|
||||||
|
|
||||||
|
# aerologic-app 的哪个分支同步到 air-cargo 的哪个分支(一对一)
|
||||||
|
SOURCE_BRANCH=main
|
||||||
|
TARGET_BRANCH=feature/hefei
|
||||||
|
|
||||||
|
# air-cargo 仓库相对 aerologic-app 根目录的位置(已在 aerologic-app 的 .gitignore 中忽略)
|
||||||
|
TARGET_SUBDIR=air-cargo
|
||||||
@@ -1,98 +1,276 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 将 SOURCE_DIR 中未提交的变更(修改/新增/删除)以 cp/rm 的方式同步到 TARGET_DIR。
|
# 将 aerologic-app 的变更以文件级 cp/rm 同步到 air-cargo,并在 air-cargo 本地提交(不 push)。
|
||||||
# 两个目录是同一份项目源码的独立 git 仓库副本,只做文件级复制/删除,不做任何内容编辑。
|
# 两个目录是同一份项目源码的独立 git 仓库副本,只做文件级复制/删除,不做任何内容编辑。
|
||||||
|
#
|
||||||
|
# 同步点记录在 air-cargo 提交信息的 trailer「Synced-From: aerologic-app@<sha>」中:
|
||||||
|
# - 区间模式(默认):基线 = air-cargo 当前分支最近一个 Synced-From,目标 = 源仓库 SOURCE_BRANCH 最新提交。
|
||||||
|
# 文件内容取自目标提交对象,与源仓库工作区无关;提交时写入新的 Synced-From,推进同步点
|
||||||
|
# - 工作区模式(--worktree):同步源仓库未提交的变更;提交时不写 Synced-From,不推进同步点
|
||||||
|
# 没有同步记录时按内容反推基线(目标第一父链上与 air-cargo HEAD 差异最少的提交),仅供确认,apply 须显式 --base
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
export GIT_LITERAL_PATHSPECS=1
|
||||||
|
|
||||||
SOURCE_DIR="${1:-$(pwd)}"
|
usage() {
|
||||||
SOURCE_DIR="$(cd "$SOURCE_DIR" && pwd)"
|
cat <<'EOF'
|
||||||
TARGET_DIR="${2:-$(dirname "$SOURCE_DIR")/air-cargo}"
|
用法: sync.sh [preview|apply] [选项]
|
||||||
MODE="${3:-preview}" # preview | apply
|
preview 只打印同步计划,不做任何改动(默认)
|
||||||
|
apply 复制/删除文件并在 air-cargo 本地提交(不 push)
|
||||||
|
选项:
|
||||||
|
--base <ref> 指定基线:没有 Synced-From 记录(首次同步)或需要纠正时使用
|
||||||
|
--to <ref> 指定目标提交,默认 config 中的 SOURCE_BRANCH
|
||||||
|
--worktree 工作区模式:同步源仓库未提交的变更,不推进同步点
|
||||||
|
--label <word> 提交信息的通用词,默认 Update(可用 Fixed、Optimized 等)
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
if [ ! -d "$SOURCE_DIR/.git" ]; then
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
echo "错误:$SOURCE_DIR 不是一个 git 仓库根目录" >&2
|
# shellcheck source=../config
|
||||||
exit 1
|
. "$SCRIPT_DIR/../config"
|
||||||
fi
|
SOURCE_DIR="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
||||||
if [ ! -d "$TARGET_DIR" ]; then
|
TARGET_DIR="$SOURCE_DIR/$TARGET_SUBDIR"
|
||||||
echo "错误:目标目录不存在:$TARGET_DIR" >&2
|
SOURCE_NAME="$(basename "$SOURCE_DIR")"
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 只同步工程项目本身的文件,Claude Code 相关的配置/记忆文件(CLAUDE.md、.claude/ 目录,
|
MODE=preview BASE_ARG="" TO_ARG="" WORKTREE=0 LABEL=Update
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
preview|apply) MODE="$1" ;;
|
||||||
|
--base) BASE_ARG="${2:?--base 缺少参数}"; shift ;;
|
||||||
|
--to) TO_ARG="${2:?--to 缺少参数}"; shift ;;
|
||||||
|
--worktree) WORKTREE=1 ;;
|
||||||
|
--label) LABEL="${2:?--label 缺少参数}"; shift ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "未知参数:$1" >&2; usage >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
die() { echo "错误:$*" >&2; exit 1; }
|
||||||
|
S() { git -C "$SOURCE_DIR" "$@"; }
|
||||||
|
T() { git -C "$TARGET_DIR" "$@"; }
|
||||||
|
short() { S rev-parse --short "$1"; }
|
||||||
|
|
||||||
|
TMP="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$TMP"' EXIT
|
||||||
|
|
||||||
|
# 只同步工程项目本身的文件,Claude Code 相关的配置/记忆文件(CLAUDE.md、CLAUDE.local.md、.claude/ 目录、根目录 .claude.* 与 .mcp.json,
|
||||||
# 包括其中的 skills/commands 等)两个仓库各自独立维护,不参与同步。
|
# 包括其中的 skills/commands 等)两个仓库各自独立维护,不参与同步。
|
||||||
is_excluded() {
|
is_excluded() {
|
||||||
local path="$1"
|
case "$1" in
|
||||||
case "$path" in
|
.claude|.claude/*|.claude.*|.mcp.json) return 0 ;;
|
||||||
.claude/*|.claude) return 0 ;;
|
CLAUDE.md|*/CLAUDE.md|CLAUDE.local.md|*/CLAUDE.local.md) return 0 ;;
|
||||||
CLAUDE.md|*/CLAUDE.md) return 0 ;;
|
|
||||||
esac
|
esac
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# --no-renames:把 rename 拆成"删除旧路径 + 新增新路径"两条记录,避免解析组合格式
|
# 列出提交中参与同步的文件「路径<TAB>blob」(已排序),用于按内容比对两个仓库
|
||||||
# --untracked-files=all:新增的整个目录也会逐个文件列出,而不是折叠成一行目录
|
filtered_tree() {
|
||||||
# -z:以 NUL 分隔,规避文件名转义/含空格的问题
|
git -C "$1" -c core.quotepath=false ls-tree -r "$2" | awk -F'\t' '
|
||||||
copy_list=()
|
$2 ~ /^\.claude($|\/|\.)/ || $2 == ".mcp.json" || $2 ~ /(^|\/)CLAUDE(\.local)?\.md$/ { next }
|
||||||
delete_list=()
|
{ split($1, m, " "); print $2 "\t" m[3] }' | LC_ALL=C sort
|
||||||
excluded_list=()
|
}
|
||||||
|
|
||||||
while IFS= read -r -d '' entry; do
|
# 两份 filtered_tree 输出中内容不同的路径(去重)
|
||||||
status="${entry:0:2}"
|
diff_paths() {
|
||||||
path="${entry:3}"
|
LC_ALL=C comm -3 "$1" "$2" | sed $'s/^\t//' | cut -f1 | LC_ALL=C sort -u
|
||||||
[ -z "$path" ] && continue
|
}
|
||||||
if is_excluded "$path"; then
|
|
||||||
excluded_list+=("$status|$path")
|
blob_at() { S rev-parse -q --verify "$1:$2" 2>/dev/null || true; }
|
||||||
continue
|
|
||||||
fi
|
# ---------- 前置检查 ----------
|
||||||
if [ -e "$SOURCE_DIR/$path" ]; then
|
[ -d "$SOURCE_DIR/.git" ] || die "$SOURCE_DIR 不是 git 仓库根目录"
|
||||||
copy_list+=("$status|$path")
|
[ -d "$TARGET_DIR/.git" ] || die "目标仓库不存在:$TARGET_DIR"
|
||||||
|
cur_branch="$(T branch --show-current)"
|
||||||
|
[ "$cur_branch" = "$TARGET_BRANCH" ] ||
|
||||||
|
die "air-cargo 当前分支为「${cur_branch:-游离 HEAD}」,应为「${TARGET_BRANCH}」(见 config),请先与用户确认"
|
||||||
|
|
||||||
|
# ---------- 确定基线与目标 ----------
|
||||||
|
BASE="" BASE_FROM="" TO="" INFERRED=0
|
||||||
|
if [ "$WORKTREE" -eq 1 ]; then
|
||||||
|
[ -z "$BASE_ARG$TO_ARG" ] || die "--worktree 不能与 --base / --to 同时使用"
|
||||||
|
BASE="$(S rev-parse HEAD)"
|
||||||
|
BASE_FROM="源仓库 HEAD(工作区模式)"
|
||||||
|
else
|
||||||
|
TO="$(S rev-parse -q --verify "${TO_ARG:-$SOURCE_BRANCH}^{commit}")" || die "源仓库找不到 ${TO_ARG:-$SOURCE_BRANCH}"
|
||||||
|
if [ -n "$BASE_ARG" ]; then
|
||||||
|
BASE="$(S rev-parse -q --verify "$BASE_ARG^{commit}")" || die "源仓库找不到基线 $BASE_ARG"
|
||||||
|
BASE_FROM="--base 参数"
|
||||||
else
|
else
|
||||||
delete_list+=("$status|$path")
|
record="$(T log -1 --grep='^Synced-From: ' --format='%(trailers:key=Synced-From,valueonly)' | sed -n '1p')"
|
||||||
|
if [ -n "$record" ]; then
|
||||||
|
BASE="$(S rev-parse -q --verify "${record##*@}^{commit}")" ||
|
||||||
|
die "air-cargo 记录的同步点 ${record##*@} 在源仓库中不存在(源仓库被 rebase / force-push 过?),请用 --base 指定"
|
||||||
|
BASE_FROM="air-cargo 提交记录中的 Synced-From"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
done < <(git -C "$SOURCE_DIR" status --porcelain=v1 -z --no-renames --untracked-files=all)
|
|
||||||
|
|
||||||
echo "源目录: $SOURCE_DIR"
|
filtered_tree "$TARGET_DIR" HEAD > "$TMP/target.tree"
|
||||||
echo "目标目录: $TARGET_DIR"
|
if [ -z "$BASE" ]; then
|
||||||
echo ""
|
# 兜底:按内容反推,在目标的第一父链上找与 air-cargo HEAD 差异最少的提交
|
||||||
echo "=== 待复制/覆盖(${#copy_list[@]})==="
|
best_n=-1
|
||||||
for item in "${copy_list[@]+"${copy_list[@]}"}"; do
|
for c in $(S rev-list --first-parent --max-count=300 "$TO"); do
|
||||||
echo " [${item%%|*}] ${item#*|}"
|
filtered_tree "$SOURCE_DIR" "$c" > "$TMP/cand.tree"
|
||||||
|
n="$(diff_paths "$TMP/cand.tree" "$TMP/target.tree" | wc -l | tr -d ' ')"
|
||||||
|
if [ "$best_n" -lt 0 ] || [ "$n" -lt "$best_n" ]; then BASE="$c"; best_n="$n"; fi
|
||||||
|
[ "$n" -eq 0 ] && break
|
||||||
|
done
|
||||||
|
[ -n "$BASE" ] || die "无法反推基线,请用 --base 指定"
|
||||||
|
INFERRED=1
|
||||||
|
BASE_FROM="按内容反推(air-cargo 无 Synced-From 记录),需确认后以 --base 传入"
|
||||||
|
fi
|
||||||
|
S merge-base --is-ancestor "$BASE" "$TO" || die "基线 $(short "$BASE") 不是目标 $(short "$TO") 的祖先"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------- 计算变更清单 ----------
|
||||||
|
copy_list=() delete_list=() excluded_list=()
|
||||||
|
classify() { # $1=状态 $2=路径 $3=同步后文件是否应存在(1/0)
|
||||||
|
[ -z "$2" ] && return
|
||||||
|
if is_excluded "$2"; then excluded_list+=("$1|$2")
|
||||||
|
elif [ "$3" -eq 1 ]; then copy_list+=("$1|$2")
|
||||||
|
else delete_list+=("$1|$2")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
if [ "$WORKTREE" -eq 1 ]; then
|
||||||
|
# --no-renames:rename 拆成「删除旧路径 + 新增新路径」;--untracked-files=all:新目录逐个文件列出
|
||||||
|
while IFS= read -r -d '' entry; do
|
||||||
|
p="${entry:3}"
|
||||||
|
if [ -e "$SOURCE_DIR/$p" ] || [ -L "$SOURCE_DIR/$p" ]; then classify "${entry:0:2}" "$p" 1; else classify "${entry:0:2}" "$p" 0; fi
|
||||||
|
done < <(S status --porcelain=v1 -z --no-renames --untracked-files=all)
|
||||||
|
else
|
||||||
|
# git diff -z 的 name-status 输出为「状态\0路径\0」交替
|
||||||
|
while IFS= read -r -d '' st && IFS= read -r -d '' p; do
|
||||||
|
if [ "$st" = D ]; then classify "$st" "$p" 0; else classify "$st" "$p" 1; fi
|
||||||
|
done < <(S diff --name-status --no-renames -z "$BASE" "$TO")
|
||||||
|
fi
|
||||||
|
|
||||||
|
new_blob() { # 同步后该路径应有的内容
|
||||||
|
if [ "$WORKTREE" -eq 1 ]; then S hash-object -- "$1"; else blob_at "$TO" "$1"; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 三方校验:air-cargo 现有内容既不等于基线、也不等于同步后的内容,说明 air-cargo 侧有独立修改,会被覆盖
|
||||||
|
conflict_list=()
|
||||||
|
for item in ${copy_list[@]+"${copy_list[@]}"} ${delete_list[@]+"${delete_list[@]}"}; do
|
||||||
|
st="${item%%|*}" p="${item#*|}"
|
||||||
|
[ -f "$TARGET_DIR/$p" ] || continue
|
||||||
|
tb="$(T hash-object -- "$p")"
|
||||||
|
[ "$tb" = "$(blob_at "$BASE" "$p")" ] && continue
|
||||||
|
[ "$st" != D ] && [ "$tb" = "$(new_blob "$p")" ] && continue
|
||||||
|
conflict_list+=("$p")
|
||||||
done
|
done
|
||||||
|
|
||||||
echo ""
|
# air-cargo 工作区只允许存在本次同步范围内的改动(如上次同步未提交,重跑会覆盖为相同内容)
|
||||||
echo "=== 待删除(${#delete_list[@]})==="
|
for item in ${copy_list[@]+"${copy_list[@]}"} ${delete_list[@]+"${delete_list[@]}"}; do
|
||||||
for item in "${delete_list[@]+"${delete_list[@]}"}"; do
|
printf '%s\n' "${item#*|}"
|
||||||
echo " [${item%%|*}] ${item#*|}"
|
done | LC_ALL=C sort -u > "$TMP/synced"
|
||||||
done
|
T status --porcelain=v1 -z --no-renames --untracked-files=all | tr '\0' '\n' | cut -c4- | LC_ALL=C sort -u > "$TMP/dirty"
|
||||||
|
unrelated="$(LC_ALL=C comm -23 "$TMP/dirty" "$TMP/synced")"
|
||||||
|
|
||||||
echo ""
|
# ---------- 提交信息 ----------
|
||||||
echo "=== 已忽略,不参与同步(${#excluded_list[@]})==="
|
printf '%s %s\n' "$LABEL" "$(date +%Y%m%d)" > "$TMP/msg"
|
||||||
for item in "${excluded_list[@]+"${excluded_list[@]}"}"; do
|
[ "$WORKTREE" -eq 1 ] || printf '\nSynced-From: %s@%s\n' "$SOURCE_NAME" "$TO" >> "$TMP/msg"
|
||||||
echo " [${item%%|*}] ${item#*|}"
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$MODE" != "apply" ]; then
|
# ---------- 打印计划 ----------
|
||||||
|
print_list() { # $1=标题 其余=「状态|路径」
|
||||||
|
local title="$1"; shift
|
||||||
echo ""
|
echo ""
|
||||||
echo "(预览模式,未执行任何操作。确认无误后加 apply 参数执行同步)"
|
echo "=== ${title}($#)==="
|
||||||
|
for item in "$@"; do echo " [${item%%|*}] ${item#*|}"; done
|
||||||
|
}
|
||||||
|
echo "源仓库: $SOURCE_DIR"
|
||||||
|
echo "目标仓库: ${TARGET_DIR}(分支 ${TARGET_BRANCH})"
|
||||||
|
if [ "$WORKTREE" -eq 1 ]; then
|
||||||
|
echo "模式: 工作区模式(同步未提交变更,不推进同步点)"
|
||||||
|
else
|
||||||
|
echo "模式: 区间模式"
|
||||||
|
echo "基线: $(short "$BASE") $(S log -1 --format=%s "$BASE")"
|
||||||
|
echo " 来源:$BASE_FROM"
|
||||||
|
echo "目标: $(short "$TO") $(S log -1 --format=%s "$TO")(${TO_ARG:-$SOURCE_BRANCH})"
|
||||||
|
echo "区间提交数: $(S rev-list --count "$BASE..$TO")"
|
||||||
|
[ "$(S rev-parse HEAD)" = "$TO" ] || echo "提示: 源仓库 HEAD 不在目标提交上,同步内容以目标提交为准"
|
||||||
|
|
||||||
|
# 内容校验:同步点记录正确时,air-cargo HEAD 与基线应只有少量元文件差异
|
||||||
|
filtered_tree "$SOURCE_DIR" "$BASE" > "$TMP/base.tree"
|
||||||
|
diff_paths "$TMP/base.tree" "$TMP/target.tree" > "$TMP/verify"
|
||||||
|
vn="$(wc -l < "$TMP/verify" | tr -d ' ')"
|
||||||
|
echo ""
|
||||||
|
echo "=== 基线校验:air-cargo HEAD 与基线有 $vn 个文件内容不同 ==="
|
||||||
|
sed -n '1,20s/^/ /p' "$TMP/verify"
|
||||||
|
[ "$vn" -le 20 ] || echo " ……(其余 $((vn - 20)) 个省略)"
|
||||||
|
[ "$vn" -le 50 ] || echo " ⚠ 差异较大:基线可能不对,或 air-cargo 分支落后/不对,请先核实"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$WORKTREE" -eq 0 ] && [ "$BASE" = "$TO" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "air-cargo 已同步到 $(short "$TO"),无需同步。"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
copied=0
|
print_list "待复制/覆盖" ${copy_list[@]+"${copy_list[@]}"}
|
||||||
deleted=0
|
print_list "待删除" ${delete_list[@]+"${delete_list[@]}"}
|
||||||
|
print_list "已忽略,不参与同步" ${excluded_list[@]+"${excluded_list[@]}"}
|
||||||
|
echo ""
|
||||||
|
echo "=== ⚠ 将覆盖 air-cargo 侧独立修改(${#conflict_list[@]})==="
|
||||||
|
for p in ${conflict_list[@]+"${conflict_list[@]}"}; do echo " $p"; done
|
||||||
|
if [ -n "$unrelated" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "=== ✖ air-cargo 工作区存在与本次同步无关的未提交改动 ==="
|
||||||
|
printf '%s\n' "$unrelated" | sed 's/^/ /'
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo "=== 提交信息 ==="
|
||||||
|
sed 's/^/ /' "$TMP/msg"
|
||||||
|
|
||||||
for item in "${copy_list[@]+"${copy_list[@]}"}"; do
|
if [ "$MODE" != apply ]; then
|
||||||
path="${item#*|}"
|
echo ""
|
||||||
mkdir -p "$(dirname "$TARGET_DIR/$path")"
|
echo "(预览模式,未执行任何操作)"
|
||||||
cp -f "$SOURCE_DIR/$path" "$TARGET_DIR/$path"
|
exit 0
|
||||||
copied=$((copied+1))
|
fi
|
||||||
done
|
|
||||||
|
|
||||||
for item in "${delete_list[@]+"${delete_list[@]}"}"; do
|
# ---------- 执行 ----------
|
||||||
path="${item#*|}"
|
[ "$INFERRED" -eq 0 ] || die "基线为反推结果,确认无误后请用 --base $(short "$BASE") 重新执行"
|
||||||
if [ -e "$TARGET_DIR/$path" ]; then
|
[ -z "$unrelated" ] || die "air-cargo 工作区存在与本次同步无关的未提交改动,请先处理"
|
||||||
rm -f "$TARGET_DIR/$path"
|
|
||||||
deleted=$((deleted+1))
|
copied=0 deleted=0
|
||||||
|
stage_list=()
|
||||||
|
for item in ${copy_list[@]+"${copy_list[@]}"}; do
|
||||||
|
p="${item#*|}" dst="$TARGET_DIR/${item#*|}"
|
||||||
|
mkdir -p "$(dirname "$dst")"
|
||||||
|
rm -f "$dst"
|
||||||
|
if [ "$WORKTREE" -eq 1 ]; then
|
||||||
|
cp -f "$SOURCE_DIR/$p" "$dst"
|
||||||
|
else
|
||||||
|
mode="$(S ls-tree "$TO" -- "$p" | cut -d' ' -f1)"
|
||||||
|
case "$mode" in
|
||||||
|
120000) ln -s "$(S cat-file blob "$TO:$p")" "$dst" ;;
|
||||||
|
160000) echo "跳过子模块:$p" >&2; continue ;;
|
||||||
|
*) S cat-file blob "$TO:$p" > "$dst"; [ "$mode" != 100755 ] || chmod +x "$dst" ;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
stage_list+=("$p")
|
||||||
|
copied=$((copied + 1))
|
||||||
done
|
done
|
||||||
|
for item in ${delete_list[@]+"${delete_list[@]}"}; do
|
||||||
|
p="${item#*|}"
|
||||||
|
if [ -e "$TARGET_DIR/$p" ] || [ -L "$TARGET_DIR/$p" ]; then
|
||||||
|
rm -f "$TARGET_DIR/$p"
|
||||||
|
deleted=$((deleted + 1))
|
||||||
|
fi
|
||||||
|
# 只暂存 air-cargo 中已跟踪的路径,否则 git add 会因 pathspec 不匹配而失败
|
||||||
|
! T ls-files --error-unmatch -- "$p" > /dev/null 2>&1 || stage_list+=("$p")
|
||||||
|
done
|
||||||
|
|
||||||
|
[ ${#stage_list[@]} -eq 0 ] || printf '%s\0' "${stage_list[@]}" | T add -A --pathspec-from-file=- --pathspec-file-nul
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "同步完成:复制/更新 $copied 个文件,删除 $deleted 个文件。"
|
echo "同步完成:复制/更新 $copied 个文件,删除 $deleted 个文件。"
|
||||||
|
if T diff --cached --quiet; then
|
||||||
|
if [ "$WORKTREE" -eq 1 ]; then
|
||||||
|
echo "air-cargo 内容已一致,无需提交。"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# 区间内只有不参与同步的文件变化时,仍提交一个空提交推进同步点
|
||||||
|
T commit -q --allow-empty -F "$TMP/msg"
|
||||||
|
else
|
||||||
|
T commit -q -F "$TMP/msg"
|
||||||
|
fi
|
||||||
|
echo "已在 air-cargo 本地提交(未 push):$(T log -1 --format='%h %s')"
|
||||||
|
T show --stat --format= HEAD | tail -1
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,8 @@
|
|||||||
# Created by https://www.toptal.com/developers/gitignore/api/androidstudio,gradle,java,kotlin
|
# Created by https://www.toptal.com/developers/gitignore/api/androidstudio,gradle,java,kotlin
|
||||||
# Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio,gradle,java,kotlin
|
# Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio,gradle,java,kotlin
|
||||||
|
|
||||||
|
air-cargo/
|
||||||
|
|
||||||
### Java ###
|
### Java ###
|
||||||
# Compiled class file
|
# Compiled class file
|
||||||
*.class
|
*.class
|
||||||
|
|||||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -8,6 +8,18 @@
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### 变更 (Changed)
|
### 变更 (Changed)
|
||||||
|
- 【手机适配分支合入主干 · 2026-10-11】`feature/phone-ui-adaptation` 合并入 `main`,此后平板与 5.5 寸手机只在
|
||||||
|
`main` 上同时开发。合并以平板业务为准:
|
||||||
|
① 主干 58 个平板提交(运抵海关放行/运抵状态筛选/海关回执、出库交接装机状态/允许装机、出港移库详情与侧滑编辑、
|
||||||
|
开始计重托盘信息与运抵重量自动计算、计重明细重量/托盘自重联动、出入港仓库清仓备注与入/出库件数重量、
|
||||||
|
进港查询出库状态筛选、ULD 来源/入库时间/所在港/位置等)全部同步到手机布局;冲突的平板布局三方合并进
|
||||||
|
`layout-sw600dp/`,ViewModel/Bean 冲突取平板业务逻辑。
|
||||||
|
② 新增手机变体:出库/入库/清仓/配运/删除申报弹框、出港移库详情编辑页(`IntExpMoveEditActivity`,替代手机专属
|
||||||
|
`IntExpMoveDetailActivity`)、共用海关回执页 `CustomsResponseActivity`;`PhoneBottomBar` 增次操作按钮。
|
||||||
|
③ 手机端对齐平板:移除平板没有的查询入参(`likeNo`、进港移库 fdate/fno/spCode、出港移库 spCode)与改写的默认值
|
||||||
|
(计重/计重记录/提取出库默认日期),补齐平板表单与筛选项(运抵分单号、进港查询出库状态、计重记录航班号、
|
||||||
|
进港移库承运人、开始计重运单号/地磅重量/平板车/航班可编辑)与操作(运抵删除申报、计重记录运抵申报、计重新增);
|
||||||
|
ULD 表单移除平板没有的进港航班号与额外必填校验。
|
||||||
- 【后端反馈联调 · 2026-10-09】后端在《问题清单》补「解决方案/备注」后,逐项(#1~#34)api-doc 核对 +
|
- 【后端反馈联调 · 2026-10-09】后端在《问题清单》补「解决方案/备注」后,逐项(#1~#34)api-doc 核对 +
|
||||||
内网直调 A/B + AeroLogic_Phone 模拟器端到端验证,并按后端意见调整前端:
|
内网直调 A/B + AeroLogic_Phone 模拟器端到端验证,并按后端意见调整前端:
|
||||||
① 后端已补字段/接口 → 前端对接:出港移库 flight/businessName/opUserName;出库交接 uldNumber 角标;
|
① 后端已补字段/接口 → 前端对接:出港移库 flight/businessName/opUserName;出库交接 uldNumber 角标;
|
||||||
|
|||||||
100
CLAUDE.md
100
CLAUDE.md
@@ -5,8 +5,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
## 项目概况
|
## 项目概况
|
||||||
|
|
||||||
**项目名称**: AirLogistics - 航空物流信息管理系统
|
**项目名称**: AirLogistics - 航空物流信息管理系统
|
||||||
**架构模式**: MVVM + 组件化 | **语言**: Kotlin 1.6.21 + Java | **版本**: 1.8.4 (versionCode 84)
|
**架构模式**: MVVM + 组件化 | **语言**: Kotlin 1.6.21 + Java | **版本**: 1.8.7 (versionCode 87)
|
||||||
**SDK**: minSdk 24 / targetSdk 30 / compileSdk 31 | **Gradle**: 7.3.3 | **JDK**: 1.8
|
**SDK**: minSdk 24 / targetSdk 30 / compileSdk 31 | **Gradle**: 7.3.3 | **JDK**: 17(`mise.toml`,运行 Gradle 用);编译目标 Java 1.8(`sourceCompatibility` / `jvmTarget`)
|
||||||
|
|
||||||
### 核心架构
|
### 核心架构
|
||||||
|
|
||||||
@@ -51,6 +51,15 @@ aerologic-app/
|
|||||||
└── 其他业务模块...
|
└── 其他业务模块...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### ⛔ `air-cargo/` 目录:一律忽略
|
||||||
|
|
||||||
|
根目录下的 `air-cargo/` 是同一份源码的另一个独立 git 仓库(远程地址不同,工作分支 `feature/hefei`),**只用于把本项目代码提交到另一个仓库**,不参与本项目任何开发;它已在 `.gitignore` 中忽略。
|
||||||
|
|
||||||
|
- 代码扫描、搜索、实现功能、排查问题、查 git 历史时**一律跳过该目录**,不读取、不修改、不当作参考实现(其中文件是同步过去的副本,可能落后于主干)
|
||||||
|
- `find` / `grep -r` 这类不认 `.gitignore` 的命令要显式排除(如 `find . -path ./air-cargo -prune -o …`、`grep -r --exclude-dir=air-cargo`);`git grep`、`rg` 默认已跳过
|
||||||
|
- 唯一允许操作它的场景是 `sync-air-cargo` skill(同步并在其本地提交,不 push)
|
||||||
|
- 其中不应存在任何 Claude / Agent 相关文件(`CLAUDE.md`、`.claude/`、`.mcp.json` 等),sync 脚本和 air-cargo 本地的 `.git/info/exclude` 都已屏蔽
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6 种典型页面类型
|
## 6 种典型页面类型
|
||||||
@@ -383,7 +392,7 @@ fun toggleAllExpand() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **子列表项 checkbox 样式**(必须使用 `_style` 系列,禁止使用 `_gray` 系列):
|
4. **子列表项 checkbox 样式**(必须复用底部“全选”的 `img_check_all_*` 资源,高度 20dp):
|
||||||
```xml
|
```xml
|
||||||
<!-- 子列表项 item_xxx_sub.xml 中的 iv_checkbox -->
|
<!-- 子列表项 item_xxx_sub.xml 中的 iv_checkbox -->
|
||||||
<ImageView
|
<ImageView
|
||||||
@@ -392,54 +401,61 @@ fun toggleAllExpand() {
|
|||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_weight="0.5"
|
android:layout_weight="0.5"
|
||||||
loadImage="@{bean.checked.get() ? @drawable/radiobtn_checked_style : @drawable/radiobtn_unchecked_style}"
|
loadImage="@{bean.checked.get() ? @drawable/img_check_all_checked : @drawable/img_check_all_unchecked}"
|
||||||
android:src="@drawable/radiobtn_unchecked_style" />
|
android:src="@drawable/img_check_all_unchecked" />
|
||||||
```
|
```
|
||||||
|
|
||||||
| 资源 | 含义 | 外观 |
|
| 资源 | 含义 | 外观 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `radiobtn_checked_style` | 选中 | colorPrimary 蓝色实心圆 + 白色内环 |
|
| `img_check_all_checked` | 选中 | `#1C8CF5` 蓝色实心圆 + 白色对勾(与底部“全选”相同)|
|
||||||
| `radiobtn_unchecked_style` | 未选中 | 透明 + 黑色边框圆 |
|
| `img_check_all_unchecked` | 未选中 | `#707070` 灰色细圆环(与底部“全选”相同)|
|
||||||
|
| ~~`radiobtn_checked_style` / `radiobtn_unchecked_style`~~ | ❌ 已废弃 | 旧版分单复选框,不再使用 |
|
||||||
| ~~`radiobtn_checked_gray`~~ | ❌ 禁用 | 灰色实心圆(错误样式) |
|
| ~~`radiobtn_checked_gray`~~ | ❌ 禁用 | 灰色实心圆(错误样式) |
|
||||||
|
|
||||||
#### 含子列表的列表项 UI 规范(以 `item_int_exp_storage_use.xml` 为基准)
|
#### 列表项 UI 规范(以 `item_int_exp_arrive / assemble / load / tally.xml` 四个基准页为准)
|
||||||
|
|
||||||
|
> 应用主题未覆盖 `textViewStyle`,未显式写 `textSize`/`textColor` 的 TextView 会落到系统默认(14sp、偏灰的次级文字色)。因此列表项里**每个** Key/Value TextView 都必须显式写 `textSize` 和 `textColor`,不能依赖“默认继承”。
|
||||||
|
|
||||||
**主列表项卡片**:
|
**主列表项卡片**:
|
||||||
|
|
||||||
| 部位 | 属性 | 标准值 |
|
| 部位 | 属性 | 标准值 |
|
||||||
|------|------|--------|
|
|------|------|--------|
|
||||||
| 外层容器 | marginHorizontal / marginTop | 10dp / 10dp |
|
| 外层容器 | marginHorizontal / marginTop | 10dp / 10dp(禁止旧写法 `marginHorizontal=15dp` + `marginVertical=5dp`)|
|
||||||
| 卡片背景 | background | `@drawable/bg_white_radius_8` |
|
| 卡片背景 | background | `@drawable/bg_white_radius_8`(`bg_item` 已废弃,不再引用)|
|
||||||
| 内容区 | padding | **10dp** |
|
| 内容区 | paddingHorizontal / paddingVertical / gravity | **10dp / 20dp** / `center_vertical` |
|
||||||
| 选中图标 | 尺寸 / marginEnd / marginTop | 40×40dp / 10dp / **0.5px**(像素级对齐)|
|
| 选中图标 | 尺寸 / marginTop | 40×40dp / **0.5px**(像素级对齐),不设 marginEnd |
|
||||||
|
| 内容列(图标右侧的竖向 LinearLayout)| layout_marginLeft | **15dp** |
|
||||||
| 选中图标 | 切换资源 | `img_plane_s`(选中)/ `img_plane`(未选中)|
|
| 选中图标 | 切换资源 | `img_plane_s`(选中)/ `img_plane`(未选中)|
|
||||||
| KV 文字 | textSize | **15sp**(Key 和 Value 均需显式设置)|
|
| KV 文字 | textSize / textColor | **15sp** / **`@color/text_normal`**(Key 和 Value 均需显式设置)|
|
||||||
| 首要字段值(运单号)| textColor | `@color/colorPrimary` |
|
| 首要字段值(运单号/ULD编码)| textColor / textStyle | `@color/colorPrimary` / **`bold`** |
|
||||||
| 其他字段 | textColor | 无需设置(继承默认 text_normal)|
|
| 状态类字段值(带颜色表达式)| textSize | **14sp** + 颜色表达式(如 `text_green / colorPrimary / text_gray`)|
|
||||||
| 两行间距 | layout_marginTop | 10dp |
|
| 两行间距 | layout_marginTop | 10dp |
|
||||||
|
| 右侧箭头(有详情页时)| `img_pda_right` | 30×30dp,`layout_marginLeft=10dp` |
|
||||||
|
|
||||||
**展开/折叠按钮(`iv_show`)**:
|
**展开/折叠按钮(`iv_show`)**:
|
||||||
|
|
||||||
| 属性 | 标准值 |
|
| 属性 | 标准值 |
|
||||||
|------|--------|
|
|------|--------|
|
||||||
| layout_width | `match_parent` |
|
| layout_width | `match_parent` |
|
||||||
| layout_height | **10dp** |
|
| layout_height | **20dp** |
|
||||||
| layout_marginTop | **-10dp**(向上收紧,紧贴卡片底边)|
|
| layout_marginTop | **-20dp**(向上收紧,紧贴卡片底边)|
|
||||||
| scaleType | **centerInside**(保证箭头不被压扁/截断)|
|
| scaleType | **center** |
|
||||||
| src | `@mipmap/img_down` |
|
| src | `@mipmap/img_down` |
|
||||||
| 显示控制 | `visible="@{bean.subList != null && !bean.subList.empty}"` |
|
| 显示控制 | `visible="@{bean.subList != null && !bean.subList.empty}"` |
|
||||||
| 不设 padding,不设 layout_marginBottom | — |
|
| 不设 padding,不设 layout_marginBottom | — |
|
||||||
|
|
||||||
> 旧版本(18dp + padding=4dp + marginBottom=5dp)已统一替换为上述新标准,参考 `item_int_exp_arrive.xml`。组装类列表若需展开后翻转箭头,附加 `android:rotation="@{bean.showMore.get() ? 180f : 0f}"`(参考 `item_int_exp_assemble.xml`)。
|
> 旧版本(18dp + padding=4dp + marginBottom=5dp,以及 10dp/-10dp/centerInside)已统一替换为上述新标准,参考 `item_int_exp_arrive.xml`。组装类列表若需展开后翻转箭头,附加 `android:rotation="@{bean.showMore.get() ? 180f : 0f}"`(参考 `item_int_exp_assemble.xml`)。
|
||||||
|
|
||||||
**子列表区域**:
|
**子列表区域**:
|
||||||
|
|
||||||
- 容器:`layout_marginTop="5dp"`,`background="#e3f6e0"`
|
- 容器:`layout_marginTop="10dp"`,`background="@drawable/bg_white_radius_8"`(旧的 `#e3f6e0` 绿色容器已废弃)
|
||||||
- 表头行:`layout_marginVertical="10dp"`,`paddingHorizontal="10dp"`
|
- 空提示:`textSize="14sp"`,`textColor="@color/text_gray"`,`paddingVertical="15dp"`
|
||||||
|
- 表头行:`background="@drawable/bg_arrive_sub_header"`,`paddingHorizontal="10dp"`,`paddingVertical="10dp"`
|
||||||
- 表头文字:`textSize="14sp"`,`textColor="@color/text_normal"`,`textStyle="bold"`,`gravity="center"`
|
- 表头文字:`textSize="14sp"`,`textColor="@color/text_normal"`,`textStyle="bold"`,`gravity="center"`
|
||||||
- 表头下方分隔线:`MaterialDivider` 高度 1px,`background="@color/c999999"`
|
- 表头下方分隔线:`MaterialDivider` 高度 1px,`dividerColor="@color/sub_list_divider"`
|
||||||
- 子列表项 padding:`paddingHorizontal="10dp"`,`paddingVertical="8dp"`
|
- 子列表项:`layout_marginVertical="8dp"`,`paddingHorizontal="10dp"`
|
||||||
- 子列表文字:`textSize="14sp"`,`textColor="@color/text_normal"`,`gravity="center"`,`layout_gravity="center_vertical"`
|
- 子列表文字:`textSize="14sp"`,`textColor="@color/text_normal"`,`gravity="center"`,`layout_gravity="center_vertical"`;首要列(运单号/分单号)`@color/colorPrimary` + `bold`;状态列 `13sp` + 颜色表达式
|
||||||
|
- 详情页里的表格式列表(如查询详情的库位/仓库 Tab)同样 14sp + `text_normal`,禁止 `@android:color/black`
|
||||||
|
|
||||||
**子列表复选框(关键)**:
|
**子列表复选框(关键)**:
|
||||||
|
|
||||||
@@ -449,11 +465,11 @@ fun toggleAllExpand() {
|
|||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_weight="0.5"
|
android:layout_weight="0.5"
|
||||||
loadImage="@{bean.checked.get() ? @drawable/radiobtn_checked_style : @drawable/radiobtn_unchecked_style}"
|
loadImage="@{bean.checked.get() ? @drawable/img_check_all_checked : @drawable/img_check_all_unchecked}"
|
||||||
android:src="@drawable/radiobtn_unchecked_style" />
|
android:src="@drawable/img_check_all_unchecked" />
|
||||||
```
|
```
|
||||||
|
|
||||||
> `loadImage` 和 `android:src` **均须**使用 `_style` 系列,**禁止**使用 `_gray` 系列(参见上方复选框样式表)
|
> `loadImage` 和 `android:src` **均须**使用 `img_check_all_checked`/`img_check_all_unchecked`(与底部“全选”同源),高度固定 **20dp**;**禁止**再使用 `radiobtn_*_style`/`radiobtn_*_gray`(参见上方复选框样式表)
|
||||||
|
|
||||||
**参考文件**: `module_gjc/.../IntExpStorageUseActivity.kt`、`IntExpStorageUseViewModel.kt`
|
**参考文件**: `module_gjc/.../IntExpStorageUseActivity.kt`、`IntExpStorageUseViewModel.kt`
|
||||||
|
|
||||||
@@ -843,6 +859,11 @@ adb logcat | grep "com.lukouguoji.aerologic" # 日志
|
|||||||
|
|
||||||
项目需同时支持 **10 寸平板横屏** 与 **5.5 寸手机竖屏**。核心原则:**业务逻辑层完全复用,只重写布局**。
|
项目需同时支持 **10 寸平板横屏** 与 **5.5 寸手机竖屏**。核心原则:**业务逻辑层完全复用,只重写布局**。
|
||||||
|
|
||||||
|
> **分支约定(2026-10-11 起)**:手机适配分支 `feature/phone-ui-adaptation` 已合并入 `main`,此后平板与手机**只在 `main` 上同时开发**,不再区分分支。合并时的状态、验证覆盖与待决事项见 `documents/双端合入主干交接文档.md`。
|
||||||
|
> **业务以平板为准**:主流程交互、表单/筛选项、接口入参一律与平板一致,手机端只做 UI 适配——平板底部批量按钮在手机上可改为卡片级操作或 `PhoneBottomBar` 次按钮(`subAction1Text/subAction2Text`),
|
||||||
|
> 但不得新增平板没有的查询入参(如 `likeNo`)、不得改写平板的默认查询值(如默认当天日期)、不得少平板的表单项。
|
||||||
|
> 平板新增/修改功能时,必须同步检查对应手机布局与该页全部弹框的手机变体。
|
||||||
|
|
||||||
### 核心机制:布局按资源限定符自动切换
|
### 核心机制:布局按资源限定符自动切换
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -1111,7 +1132,8 @@ override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) =
|
|||||||
|
|
||||||
- ✅ **已完成**:适配框架 + 手机端公共资源 + 7 个手机版公共组件
|
- ✅ **已完成**:适配框架 + 手机端公共资源 + 7 个手机版公共组件
|
||||||
- ✅ **已完成页面**:
|
- ✅ **已完成页面**:
|
||||||
- 「国际出港移库」`IntExpMoveActivity` + 「运单详情」`IntExpMoveDetailActivity`(框架参考页)
|
- 「国际出港移库」`IntExpMoveActivity`(框架参考页);详情/编辑与平板共用 `IntExpMoveEditActivity`
|
||||||
|
(手机布局为设计稿「运单详情」样式,卡片「查看详情」/「编辑」分别对应平板点击卡片/侧滑编辑)
|
||||||
- 「国际出港出库交接」`IntExpOutHandoverActivity` + 「板箱详情」`IntExpOutHandoverDetailActivity`
|
- 「国际出港出库交接」`IntExpOutHandoverActivity` + 「板箱详情」`IntExpOutHandoverDetailActivity`
|
||||||
(公共组件首个完整落地页,双端实机验证通过)
|
(公共组件首个完整落地页,双端实机验证通过)
|
||||||
- 「ULD管理」`UldListActivity` + `UldEditActivity`(app 模块首个适配页;一套编辑布局覆盖
|
- 「ULD管理」`UldListActivity` + `UldEditActivity`(app 模块首个适配页;一套编辑布局覆盖
|
||||||
@@ -1120,14 +1142,14 @@ override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) =
|
|||||||
FAB 进记录)、`GjcWeighingStartActivity`(开始计重,托盘数量/自重按设计稿手输)、
|
FAB 进记录)、`GjcWeighingStartActivity`(开始计重,托盘数量/自重按设计稿手输)、
|
||||||
`GjcWeighingRecordListActivity`(计重记录)、`GjcWeighingRecordDetailsActivity`(计重明细,
|
`GjcWeighingRecordListActivity`(计重记录)、`GjcWeighingRecordDetailsActivity`(计重明细,
|
||||||
isEditMode 复用为手机全屏「修改记录」覆层)+ 手机专属新页 `GjcWeighingWbDetailActivity`(运单详情)。
|
isEditMode 复用为手机全屏「修改记录」覆层)+ 手机专属新页 `GjcWeighingWbDetailActivity`(运单详情)。
|
||||||
搜索分流规则(实测):含字母→fno、11 位数字→wbNo、其余→likeNo(likeNo 按 8 位 no 匹配并非模糊);
|
合并搜索框只分流到平板同名入参:含字母→fno、其余→wbNo;
|
||||||
checkIn 过滤等 7 项后端缺口见问题清单 #12~#18
|
checkIn 过滤等 7 项后端缺口见问题清单 #12~#18
|
||||||
- 「出港运抵」主列表 + 3 个手机专属新页:`IntExpArriveActivity`(待运抵/已运抵 Tab,客户端按
|
- 「出港运抵」主列表 + 3 个手机专属新页:`IntExpArriveActivity`(待运抵/已运抵 Tab,客户端按
|
||||||
`declareStatus` 是否为空拆分数据集,卡片长按浮层:待运抵→补充、已运抵→重置+回执)、
|
`declareStatus` 是否为空拆分数据集,卡片长按浮层仅已运抵:重置 / 删除申报 / 回执)、
|
||||||
`IntExpArriveSubOrderActivity`(分单明细,数据直接复用主列表已加载的 `haWbList`,零新增请求)、
|
`IntExpArriveSubOrderActivity`(分单明细,数据直接复用主列表已加载的 `haWbList`,零新增请求)、
|
||||||
`IntExpArriveReceiptActivity`(回执内容只读页,复用 `haWbList[].response`)、
|
`IntExpArriveReceiptActivity`(分单回执只读页,复用 `haWbList[].response`;主单「回执」与平板侧滑同走
|
||||||
`IntExpArriveSupplementActivity`(补充信息,参照 Pad 端国际进港「收发货人信息」批量应用模式,
|
`getResponse` → 共用 `CustomsResponseActivity`)。
|
||||||
后端接口不存在,实机 404 已确认,见问题清单 #19)。
|
(原手机专属「补充信息」页 `IntExpArriveSupplementActivity` 因后端无接口(#19)已于 2026-10-09 删除。)
|
||||||
Tab 过滤两个坑:① **RecyclerView 里 `View.GONE` 不会让 item 塌陷为 0 高度**(不同于普通
|
Tab 过滤两个坑:① **RecyclerView 里 `View.GONE` 不会让 item 塌陷为 0 高度**(不同于普通
|
||||||
ViewGroup),必须在数据层整体 `refresh()` 过滤后的子集给 adapter,不能给 item 根节点加
|
ViewGroup),必须在数据层整体 `refresh()` 过滤后的子集给 adapter,不能给 item 根节点加
|
||||||
visibility 绑定(实机截图验证过,见「常见编译错误速查」);② 通过 `Intent.putExtra(Serializable)`
|
visibility 绑定(实机截图验证过,见「常见编译错误速查」);② 通过 `Intent.putExtra(Serializable)`
|
||||||
@@ -1536,14 +1558,16 @@ Glide.with(itemView.context).load(glideUrl).into(binding.ivThumbnail)
|
|||||||
### 双端验证命令
|
### 双端验证命令
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
~/Library/Android/sdk/emulator/emulator -list-avds # 可用:Aerologic_Phone_5.5_API31(手机,首选)、Aerologic_Tablet(平板)、Medium_Phone_API_36.1
|
/opt/homebrew/share/android-commandlinetools/emulator/emulator -list-avds # 可用:AeroLogic_Phone(手机)、AeroLogic_Tablet(平板)
|
||||||
adb -s <设备> shell wm size && adb -s <设备> shell wm density # 确认屏幕参数
|
adb -s <设备> shell wm size && adb -s <设备> shell wm density # 确认屏幕参数
|
||||||
adb -s <设备> logcat -d -s BaseActivity:D # 查看形态识别结果 PHONE(sw=384dp) / TABLET(sw=800dp)
|
adb -s <设备> logcat -d -s BaseActivity:D # 查看形态识别结果 PHONE(sw=394dp) / TABLET(sw=800dp)
|
||||||
adb -s <设备> exec-out screencap -p > /tmp/shot.png # 截图比对
|
adb -s <设备> exec-out screencap -p > /tmp/shot.png # 截图比对
|
||||||
```
|
```
|
||||||
|
|
||||||
> 手机端验证**首选 `Aerologic_Phone_5.5_API31`**:按真实交付手持机 1:1 定制——720×1440、5.51 寸(300dpi,sw=384dp)、
|
> 手机端用 **`AeroLogic_Phone`**:按真实交付手持机 1:1 定制——720×1440、5.51 寸(292dpi,sw=394dp)、
|
||||||
> Android 12 / API 31(与 targetSdk 一致)、arm64-v8a,与实机的差异最小。`Medium_Phone_API_36.1`(1080×2400、API 36)
|
> Android 12 / API 31、arm64-v8a,与实机的差异最小。
|
||||||
> 仅作兼容性参考,分辨率与系统版本都和交付机不符。
|
> 平板端用 **`AeroLogic_Tablet`**:1280×800、160dpi(sw=800dp)、横屏、API 31。平板模拟器没有物理按键,
|
||||||
|
> `adb shell monkey` 拉起会失败,改用 `am start -n com.lukouguoji.aerologic/com.lukouguoji.module_base.LoginActivity`。
|
||||||
|
> 模拟器可执行文件在 Homebrew 的 android-commandlinetools 下(见上方命令),不在 `~/Library/Android/sdk`。
|
||||||
|
|
||||||
> 改动 `BaseActivity` / `BaseBindingActivity` / 公共组件后,**必须在平板上回归**,确认现有平板版行为不变。
|
> 改动 `BaseActivity` / `BaseBindingActivity` / 公共组件后,**必须在平板上回归**,确认现有平板版行为不变。
|
||||||
|
|||||||
@@ -20,11 +20,6 @@
|
|||||||
android:exported="true"
|
android:exported="true"
|
||||||
tools:replace="android:exported" />
|
tools:replace="android:exported" />
|
||||||
|
|
||||||
<!-- 出港移库-运单详情(手机版适配验证页) -->
|
|
||||||
<activity
|
|
||||||
android:name="com.lukouguoji.gjc.page.move.IntExpMoveDetailActivity"
|
|
||||||
android:exported="true"
|
|
||||||
tools:replace="android:exported" />
|
|
||||||
|
|
||||||
<!-- 出库交接(手机版适配验证页) -->
|
<!-- 出库交接(手机版适配验证页) -->
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -246,9 +246,9 @@
|
|||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:screenOrientation="unspecified" />
|
android:screenOrientation="unspecified" />
|
||||||
<!-- 国际出港-出港移库-运单详情(手机版专属) -->
|
<!-- 国际出港-出港移库编辑/详情 -->
|
||||||
<activity
|
<activity
|
||||||
android:name="com.lukouguoji.gjc.page.move.IntExpMoveDetailActivity"
|
android:name="com.lukouguoji.gjc.page.move.IntExpMoveEditActivity"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:screenOrientation="unspecified" />
|
android:screenOrientation="unspecified" />
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import com.lukouguoji.module_base.bean.FlightBean
|
|||||||
import com.lukouguoji.module_base.common.Constant
|
import com.lukouguoji.module_base.common.Constant
|
||||||
import com.lukouguoji.module_base.http.net.NetApply
|
import com.lukouguoji.module_base.http.net.NetApply
|
||||||
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
||||||
|
import com.lukouguoji.module_base.util.DictUtils
|
||||||
|
import dev.utils.app.info.KeyValue
|
||||||
|
|
||||||
class FlightQueryDetailsViewModel : BaseViewModel() {
|
class FlightQueryDetailsViewModel : BaseViewModel() {
|
||||||
|
|
||||||
@@ -14,9 +16,24 @@ class FlightQueryDetailsViewModel : BaseViewModel() {
|
|||||||
|
|
||||||
var dataBean = MutableLiveData<FlightBean>()
|
var dataBean = MutableLiveData<FlightBean>()
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// 枚举字段展示文案:接口已返回名称时原样显示,只返回值(code)时按字典反查名称
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
val countryTypeText = MutableLiveData("")
|
||||||
|
val serviceTypeText = MutableLiveData("")
|
||||||
|
val portCodeText = MutableLiveData("")
|
||||||
|
val flightStatusText = MutableLiveData("")
|
||||||
|
|
||||||
|
private var countryTypeDict = emptyList<KeyValue>()
|
||||||
|
private var serviceTypeDict = emptyList<KeyValue>()
|
||||||
|
private var portCodeDict = emptyList<KeyValue>()
|
||||||
|
private var flightStatusDict = emptyList<KeyValue>()
|
||||||
|
|
||||||
fun initOnCreated(intent: Intent) {
|
fun initOnCreated(intent: Intent) {
|
||||||
id = intent.getStringExtra(Constant.Key.ID) ?: ""
|
id = intent.getStringExtra(Constant.Key.ID) ?: ""
|
||||||
|
|
||||||
|
loadDictLists()
|
||||||
getData()
|
getData()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,8 +44,52 @@ class FlightQueryDetailsViewModel : BaseViewModel() {
|
|||||||
onSuccess = {
|
onSuccess = {
|
||||||
it.data?.let { data ->
|
it.data?.let { data ->
|
||||||
dataBean.value = data
|
dataBean.value = data
|
||||||
|
refreshEnumText()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* 加载四个枚举字典(与新增/编辑页同源),用于把 code 反查成 label
|
||||||
|
*/
|
||||||
|
private fun loadDictLists() {
|
||||||
|
DictUtils.getCountryTypeList(addAll = false) {
|
||||||
|
countryTypeDict = it
|
||||||
|
refreshEnumText()
|
||||||
|
}
|
||||||
|
DictUtils.getFlightServiceTypeList(addAll = false) {
|
||||||
|
serviceTypeDict = it
|
||||||
|
refreshEnumText()
|
||||||
|
}
|
||||||
|
DictUtils.getFlightPortCodeList(addAll = false) {
|
||||||
|
portCodeDict = it
|
||||||
|
refreshEnumText()
|
||||||
|
}
|
||||||
|
DictUtils.getFlightStatusList(addAll = false) {
|
||||||
|
flightStatusDict = it
|
||||||
|
refreshEnumText()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据或字典任一就绪都重算一次,避免两者返回先后顺序影响展示
|
||||||
|
*/
|
||||||
|
private fun refreshEnumText() {
|
||||||
|
val bean = dataBean.value ?: return
|
||||||
|
countryTypeText.value = toLabel(countryTypeDict, bean.countryType)
|
||||||
|
serviceTypeText.value = toLabel(serviceTypeDict, bean.serviceType)
|
||||||
|
portCodeText.value = toLabel(portCodeDict, bean.portCode)
|
||||||
|
flightStatusText.value = toLabel(flightStatusDict, bean.flightStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 值转 label:先按 code 反查,命中不了再判断是否本身就是 label,仍不命中则原样返回
|
||||||
|
*/
|
||||||
|
private fun toLabel(dict: List<KeyValue>, raw: String?): String {
|
||||||
|
if (raw.isNullOrEmpty()) return ""
|
||||||
|
dict.firstOrNull { it.value == raw }?.let { return it.key }
|
||||||
|
dict.firstOrNull { it.key == raw }?.let { return it.key }
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,9 +63,7 @@ class GnjMoveStashListActivity :
|
|||||||
|
|
||||||
/** 筛选:重置(只清空弹层内条件,不影响 Tab 与顶部搜索框) */
|
/** 筛选:重置(只清空弹层内条件,不影响 Tab 与顶部搜索框) */
|
||||||
fun resetFilter() {
|
fun resetFilter() {
|
||||||
viewModel.flightDate.value = ""
|
viewModel.carrier.value = ""
|
||||||
viewModel.flightNo.value = ""
|
|
||||||
viewModel.spCode.value = ""
|
|
||||||
viewModel.businessType.value = ""
|
viewModel.businessType.value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,12 +54,6 @@ class GnjMoveStashListViewModel : BasePageViewModel() {
|
|||||||
/** 移库状态 Tab:"0" 待移库(DomImpMove/search)/ "1" 已移库(DomImpMove/searchMoved) */
|
/** 移库状态 Tab:"0" 待移库(DomImpMove/search)/ "1" 已移库(DomImpMove/searchMoved) */
|
||||||
val moveState = MutableLiveData("0")
|
val moveState = MutableLiveData("0")
|
||||||
|
|
||||||
/** 手机筛选弹层:航班日期 / 航班号 / 特码(默认空 → 不进请求) */
|
|
||||||
val flightDate = MutableLiveData("")
|
|
||||||
val flightNo = MutableLiveData("")
|
|
||||||
val spCode = MutableLiveData("")
|
|
||||||
val spCodeList = MutableLiveData<List<KeyValue>>(emptyList())
|
|
||||||
|
|
||||||
val moveStateTabs = MutableLiveData(
|
val moveStateTabs = MutableLiveData(
|
||||||
listOf(KeyValue("待移库", "0"), KeyValue("已移库", "1"))
|
listOf(KeyValue("待移库", "0"), KeyValue("已移库", "1"))
|
||||||
)
|
)
|
||||||
@@ -73,15 +67,11 @@ class GnjMoveStashListViewModel : BasePageViewModel() {
|
|||||||
/** 全选状态(手机 PhoneBottomBar 勾选图标) */
|
/** 全选状态(手机 PhoneBottomBar 勾选图标) */
|
||||||
val isAllChecked = MutableLiveData(false)
|
val isAllChecked = MutableLiveData(false)
|
||||||
|
|
||||||
/** 手机专属的额外请求(Tab 角标、特码字典)开关,仅手机形态在 Activity 中开启 */
|
/** 手机专属的额外请求(Tab 角标)开关,仅手机形态在 Activity 中开启 */
|
||||||
private var phoneExtrasEnabled = false
|
private var phoneExtrasEnabled = false
|
||||||
|
|
||||||
fun initPhoneExtras() {
|
fun initPhoneExtras() {
|
||||||
phoneExtrasEnabled = true
|
phoneExtrasEnabled = true
|
||||||
// 特码字典:flag=0 国内、ieFlag=I 进港(是否生效待实测,见问题清单)
|
|
||||||
DictUtils.getSpecialCodeList(addAll = false, flag = 0, ieFlag = "I") { list ->
|
|
||||||
spCodeList.postValue(listOf(KeyValue("全部", "")) + list)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@@ -135,10 +125,6 @@ class GnjMoveStashListViewModel : BasePageViewModel() {
|
|||||||
"awbType" to businessType.value!!.ifEmpty { null },
|
"awbType" to businessType.value!!.ifEmpty { null },
|
||||||
"wbNo" to waybill.value!!.ifEmpty { null },
|
"wbNo" to waybill.value!!.ifEmpty { null },
|
||||||
"carrier" to carrier.value!!.ifEmpty { null },
|
"carrier" to carrier.value!!.ifEmpty { null },
|
||||||
// 以下为手机筛选弹层字段,平板恒为空串 → 恒传 null,行为不变
|
|
||||||
"fdate" to flightDate.value?.ifEmpty { null },
|
|
||||||
"fno" to flightNo.value?.ifEmpty { null },
|
|
||||||
"spCode" to spCode.value?.ifEmpty { null },
|
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun getData() {
|
override fun getData() {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class GnjUnloadAddActivity :
|
|||||||
override fun viewModelClass() = GnjUnloadAddViewModel::class.java
|
override fun viewModelClass() = GnjUnloadAddViewModel::class.java
|
||||||
|
|
||||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||||
setBackArrow("国内进港开始卸机")
|
setBackArrow("进港开始卸机")
|
||||||
|
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
viewModel.rv = binding.rv
|
viewModel.rv = binding.rv
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class GnjUnloadListActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||||
setBackArrow("国内进港卸机")
|
setBackArrow("进港卸机")
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
viewModel.pageModel.bindSmartRefreshLayout(
|
viewModel.pageModel.bindSmartRefreshLayout(
|
||||||
binding.srl,
|
binding.srl,
|
||||||
|
|||||||
@@ -31,19 +31,19 @@ class UldEditViewModel : BaseViewModel() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// 所属航司下拉列表(数据源:POST /typeCode/intCarrier)
|
// 来源下拉列表
|
||||||
val carrierList = MutableLiveData<List<KeyValue>>(emptyList())
|
val sourceList = MutableLiveData<List<KeyValue>>(
|
||||||
|
|
||||||
// ========== 手机版(5.5 寸手持端)专用 ==========
|
|
||||||
|
|
||||||
/** 来源选项(与平板端一致:空运 / 陆运) */
|
|
||||||
val sourceEditList = MutableLiveData<List<KeyValue>>(
|
|
||||||
listOf(
|
listOf(
|
||||||
KeyValue("空运", "空运"),
|
KeyValue("空运", "空运"),
|
||||||
KeyValue("陆运", "陆运"),
|
KeyValue("陆运", "陆运"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 所属航司下拉列表(数据源:POST /typeCode/intCarrier)
|
||||||
|
val carrierList = MutableLiveData<List<KeyValue>>(emptyList())
|
||||||
|
|
||||||
|
// ========== 手机版(5.5 寸手持端)专用 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手机版状态选项。后端枚举维持两态(0 正常 / 1 故障,不新增「占用」),
|
* 手机版状态选项。后端枚举维持两态(0 正常 / 1 故障,不新增「占用」),
|
||||||
* 按设计稿文案映射 可用=0 / 维修=1。
|
* 按设计稿文案映射 可用=0 / 维修=1。
|
||||||
@@ -60,12 +60,9 @@ class UldEditViewModel : BaseViewModel() {
|
|||||||
else if (modify) DetailsPageType.Modify else DetailsPageType.Details
|
else if (modify) DetailsPageType.Modify else DetailsPageType.Details
|
||||||
|
|
||||||
if (id.isEmpty()) {
|
if (id.isEmpty()) {
|
||||||
// 新增:直接加载航司列表
|
// 新增:所在港默认 HFE,直接加载航司列表
|
||||||
|
uldBean.value = ULDBean(nowPort = "HFE")
|
||||||
loadCarrierList(null)
|
loadCarrierList(null)
|
||||||
// 手机版新增默认所在港 HFE(设计稿约定);平板无此预填,行为不变
|
|
||||||
if (DeviceUtil.isPhone()) {
|
|
||||||
uldBean.value = uldBean.value?.copy(nowPort = "HFE")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
getData()
|
getData()
|
||||||
}
|
}
|
||||||
@@ -101,26 +98,6 @@ class UldEditViewModel : BaseViewModel() {
|
|||||||
showToast("ULD编号不能为空")
|
showToast("ULD编号不能为空")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 手机版按设计稿校验必填项(自重 / 入库时间);平板表单无入库时间项,保持原校验行为
|
|
||||||
if (DeviceUtil.isPhone()) {
|
|
||||||
if (bean.uldWeight.isBlank()) {
|
|
||||||
showToast("自重不能为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// saveUld 缺最大装载容积/最大载重时后端直接 500(内网实测),提前拦截
|
|
||||||
if (bean.maxVolume.isBlank()) {
|
|
||||||
showToast("最大装载容积不能为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (bean.maxWeight.isBlank()) {
|
|
||||||
showToast("最大载重不能为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (bean.inStoreTime.isBlank()) {
|
|
||||||
showToast("入库时间不能为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 手机版日期行只产出 yyyy-MM-dd,而 inStoreTime 为 date-time(仅日期后端返回 400),补齐时分秒
|
// 手机版日期行只产出 yyyy-MM-dd,而 inStoreTime 为 date-time(仅日期后端返回 400),补齐时分秒
|
||||||
if (DeviceUtil.isPhone() && bean.inStoreTime.length == 10) {
|
if (DeviceUtil.isPhone() && bean.inStoreTime.length == 10) {
|
||||||
bean.inStoreTime = "${bean.inStoreTime} 00:00:00"
|
bean.inStoreTime = "${bean.inStoreTime} 00:00:00"
|
||||||
@@ -132,12 +109,7 @@ class UldEditViewModel : BaseViewModel() {
|
|||||||
}) {
|
}) {
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
showToast(it.msg.noNull(if (isAdd) "新增成功" else "修改成功"))
|
showToast(it.msg.noNull(if (isAdd) "新增成功" else "修改成功"))
|
||||||
if (isAdd) {
|
if (isAdd) uldBean.value = ULDBean(nowPort = "HFE") else getData()
|
||||||
// 清空表单继续录入;手机版保持所在港默认 HFE(与进入页面时一致)
|
|
||||||
uldBean.value = if (DeviceUtil.isPhone()) ULDBean(nowPort = "HFE") else ULDBean()
|
|
||||||
} else {
|
|
||||||
getData()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ class HomeFragment : Fragment() {
|
|||||||
.navigation()
|
.navigation()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 理货报告
|
// 进港理货
|
||||||
Constant.AuthName.IntImpTally -> {
|
Constant.AuthName.IntImpTally -> {
|
||||||
ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_INT_IMP_TALLY)
|
ARouter.getInstance().build(ARouterConstants.ACTIVITY_URL_INT_IMP_TALLY)
|
||||||
.navigation()
|
.navigation()
|
||||||
@@ -847,7 +847,7 @@ class HomeFragment : Fragment() {
|
|||||||
RightMenu(
|
RightMenu(
|
||||||
Constant.AuthName.IntImpTally,
|
Constant.AuthName.IntImpTally,
|
||||||
R.mipmap.gjj_li_huo_bao_gao_icon,
|
R.mipmap.gjj_li_huo_bao_gao_icon,
|
||||||
"理货报告"
|
"进港理货"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
list.add(
|
list.add(
|
||||||
|
|||||||
@@ -125,6 +125,49 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Row 3: 来源 / 入库时间 / 所在港 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||||
|
hint='@{"请选择来源"}'
|
||||||
|
list="@{viewModel.sourceList}"
|
||||||
|
title='@{"来源"}'
|
||||||
|
titleLength="@{7}"
|
||||||
|
type="@{DataLayoutType.SPINNER}"
|
||||||
|
value='@={viewModel.uldBean.source}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||||
|
hint='@{"请选择入库时间"}'
|
||||||
|
title='@{"入库时间"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.DATETIME}"
|
||||||
|
value='@={viewModel.uldBean.inStoreTime}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||||
|
hint='@{"请输入所在港"}'
|
||||||
|
title='@{"所在港"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@={viewModel.uldBean.nowPort}' />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 备注(全行,多行输入) -->
|
<!-- 备注(全行,多行输入) -->
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="0.7"
|
android:layout_weight="0.8"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1.5"
|
android:layout_weight="1.0"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="自重:"
|
android:text="自重:"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
completeSpace="@{3}" />
|
completeSpace="@{4}" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
<!-- 第二行:最大载重 | 备注 -->
|
<!-- 第二行:最大载重 | 来源 | 所在港 | 位置 | 入库时间 -->
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1.5"
|
android:layout_weight="1.0"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -193,24 +193,90 @@
|
|||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
<!-- 备注 -->
|
<!-- 来源 -->
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="3.6"
|
android:layout_weight="0.8"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="备注:"
|
android:text="来源:"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
completeSpace="@{3}" />
|
completeSpace="@{3}" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@{bean.remark}"
|
android:text="@{bean.source}"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
<!-- 所在港 -->
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0.8"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="所在港:"
|
||||||
|
android:textSize="15sp"
|
||||||
|
completeSpace="@{4}" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{bean.nowPort}"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
<!-- 位置 -->
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0.8"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="位置:"
|
||||||
|
android:textSize="15sp"
|
||||||
|
completeSpace="@{5}" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{bean.nowPosition}"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
<!-- 入库时间 -->
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1.2"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="入库时间:"
|
||||||
|
android:textSize="15sp"
|
||||||
|
completeSpace="@{7}" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{bean.inStoreTime}"
|
||||||
android:textSize="15sp" />
|
android:textSize="15sp" />
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="15dp">
|
android:padding="15dp">
|
||||||
|
|
||||||
<!-- 第1行:航班日期、航班号、航程 -->
|
<!-- 第1行:航班日期、航班号、机型 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -68,14 +68,53 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
enable="@{false}"
|
enable="@{false}"
|
||||||
title='@{"航程"}'
|
title='@{"机型"}'
|
||||||
titleLength="@{4}"
|
titleLength="@{4}"
|
||||||
type="@{DataLayoutType.INPUT}"
|
type="@{DataLayoutType.INPUT}"
|
||||||
value='@{viewModel.dataBean.range}' />
|
value='@{viewModel.dataBean.aircraftCode}' />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 第2行:地区类型、服务类型、经停站 -->
|
<!-- 第2行:始发港、经停港、目的港 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"始发港"}'
|
||||||
|
titleLength="@{4}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.fdep}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"经停港"}'
|
||||||
|
titleLength="@{4}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.jtz}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"目的港"}'
|
||||||
|
titleLength="@{4}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.fdest}' />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 第3行:地区类型、服务类型 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -90,7 +129,7 @@
|
|||||||
title='@{"地区类型"}'
|
title='@{"地区类型"}'
|
||||||
titleLength="@{4}"
|
titleLength="@{4}"
|
||||||
type="@{DataLayoutType.INPUT}"
|
type="@{DataLayoutType.INPUT}"
|
||||||
value='@{viewModel.dataBean.countryType}' />
|
value='@{viewModel.countryTypeText}' />
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@@ -100,21 +139,55 @@
|
|||||||
title='@{"服务类型"}'
|
title='@{"服务类型"}'
|
||||||
titleLength="@{4}"
|
titleLength="@{4}"
|
||||||
type="@{DataLayoutType.INPUT}"
|
type="@{DataLayoutType.INPUT}"
|
||||||
value='@{viewModel.dataBean.serviceType}' />
|
value='@{viewModel.serviceTypeText}' />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 第4行:飞行状态、航班状态、延误原因 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
enable="@{false}"
|
enable="@{false}"
|
||||||
title='@{"经停站"}'
|
title='@{"飞行状态"}'
|
||||||
titleLength="@{4}"
|
titleLength="@{4}"
|
||||||
type="@{DataLayoutType.INPUT}"
|
type="@{DataLayoutType.INPUT}"
|
||||||
value='@{viewModel.dataBean.jtz}' />
|
value='@{viewModel.portCodeText}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"航班状态"}'
|
||||||
|
titleLength="@{4}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.flightStatusText}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"延误原因"}'
|
||||||
|
titleLength="@{4}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.delayFreeText}' />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 第3行:计划起飞、预计起飞、实际起飞 -->
|
<!-- 第5行:计划起飞、预计起飞、实际起飞 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -153,7 +226,7 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 第4行:计划降落、预计降落、实际降落 -->
|
<!-- 第6行:计划降落、预计降落、实际降落 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -192,72 +265,13 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 第5行:飞行状态、航班状态、延误原因 -->
|
<!-- 第7行:机位、机号 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
enable="@{false}"
|
|
||||||
title='@{"飞行状态"}'
|
|
||||||
titleLength="@{4}"
|
|
||||||
type="@{DataLayoutType.INPUT}"
|
|
||||||
value='@{viewModel.dataBean.portCode}' />
|
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
enable="@{false}"
|
|
||||||
title='@{"航班状态"}'
|
|
||||||
titleLength="@{4}"
|
|
||||||
type="@{DataLayoutType.INPUT}"
|
|
||||||
value='@{viewModel.dataBean.flightStatus}' />
|
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
enable="@{false}"
|
|
||||||
title='@{"延误原因"}'
|
|
||||||
titleLength="@{4}"
|
|
||||||
type="@{DataLayoutType.INPUT}"
|
|
||||||
value='@{viewModel.dataBean.delayFreeText}' />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- 第6行:机号、机型、机位 -->
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
enable="@{false}"
|
|
||||||
title='@{"机号"}'
|
|
||||||
titleLength="@{4}"
|
|
||||||
type="@{DataLayoutType.INPUT}"
|
|
||||||
value='@{viewModel.dataBean.registration}' />
|
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
enable="@{false}"
|
|
||||||
title='@{"机型"}'
|
|
||||||
titleLength="@{4}"
|
|
||||||
type="@{DataLayoutType.INPUT}"
|
|
||||||
value='@{viewModel.dataBean.aircraftCode}' />
|
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -268,6 +282,21 @@
|
|||||||
type="@{DataLayoutType.INPUT}"
|
type="@{DataLayoutType.INPUT}"
|
||||||
value='@{viewModel.dataBean.standId}' />
|
value='@{viewModel.dataBean.standId}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"机号"}'
|
||||||
|
titleLength="@{4}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.registration}' />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -121,33 +121,14 @@
|
|||||||
setOnResetClick="@{(v)-> activity.resetFilter()}"
|
setOnResetClick="@{(v)-> activity.resetFilter()}"
|
||||||
title='@{"筛选条件"}'>
|
title='@{"筛选条件"}'>
|
||||||
|
|
||||||
<!-- 航班日期(入参 fdate) -->
|
<!-- 承运人(入参 carrier,与平板搜索栏一致) -->
|
||||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
hint='@{"请选择航班日期"}'
|
hint='@{"请输入承运人"}'
|
||||||
title='@{"航班日期"}'
|
title='@{"承运人"}'
|
||||||
type="@{PhoneDataLayoutType.DATE}"
|
|
||||||
value="@={viewModel.flightDate}" />
|
|
||||||
|
|
||||||
<!-- 航班号(入参 fno) -->
|
|
||||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
hint='@{"请输入航班号"}'
|
|
||||||
title='@{"航班号"}'
|
|
||||||
type="@{PhoneDataLayoutType.INPUT}"
|
type="@{PhoneDataLayoutType.INPUT}"
|
||||||
value="@={viewModel.flightNo}" />
|
value="@={viewModel.carrier}" />
|
||||||
|
|
||||||
<!-- 特码(入参 spCode) -->
|
|
||||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
hint='@{"请选择特码"}'
|
|
||||||
list="@{viewModel.spCodeList}"
|
|
||||||
title='@{"特码"}'
|
|
||||||
type="@{PhoneDataLayoutType.SPINNER}"
|
|
||||||
value="@={viewModel.spCode}" />
|
|
||||||
|
|
||||||
<!-- 运单类型(入参 awbType,复用平板既有 businessType 字段与字典) -->
|
<!-- 运单类型(入参 awbType,复用平板既有 businessType 字段与字典) -->
|
||||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||||
|
|||||||
@@ -167,46 +167,34 @@
|
|||||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||||
hint='@{"请输入自重"}'
|
hint='@{"请输入自重"}'
|
||||||
numeric="@{true}"
|
numeric="@{true}"
|
||||||
required="@{viewModel.pageType == DetailsPageType.Add}"
|
|
||||||
title='@{"自重(kg)"}'
|
title='@{"自重(kg)"}'
|
||||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
||||||
value="@={viewModel.uldBean.uldWeight}" />
|
value="@={viewModel.uldBean.uldWeight}" />
|
||||||
|
|
||||||
<!-- 最大装载容积:新增/修改可编辑(与平板一致;saveUld 缺该字段后端 500,内网实测) -->
|
<!-- 最大装载容积 -->
|
||||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||||
hint='@{"请输入最大装载容积"}'
|
hint='@{"请输入最大装载容积"}'
|
||||||
numeric="@{true}"
|
numeric="@{true}"
|
||||||
required="@{viewModel.pageType == DetailsPageType.Add}"
|
|
||||||
title='@{"最大装载容积(m³)"}'
|
title='@{"最大装载容积(m³)"}'
|
||||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
||||||
value="@={viewModel.uldBean.maxVolume}" />
|
value="@={viewModel.uldBean.maxVolume}" />
|
||||||
|
|
||||||
<!-- 最大载重:新增/修改可编辑(与平板一致;saveUld 缺该字段后端 500,内网实测) -->
|
<!-- 最大载重 -->
|
||||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||||
hint='@{"请输入最大载重"}'
|
hint='@{"请输入最大载重"}'
|
||||||
numeric="@{true}"
|
numeric="@{true}"
|
||||||
required="@{viewModel.pageType == DetailsPageType.Add}"
|
|
||||||
title='@{"最大载重(kg)"}'
|
title='@{"最大载重(kg)"}'
|
||||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
||||||
value="@={viewModel.uldBean.maxWeight}" />
|
value="@={viewModel.uldBean.maxWeight}" />
|
||||||
|
|
||||||
<!-- 进港航班号(ifNo) -->
|
|
||||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
|
||||||
hint='@{"请输入进港航班号"}'
|
|
||||||
title='@{"进港航班号"}'
|
|
||||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
|
||||||
value="@={viewModel.uldBean.ifNo}" />
|
|
||||||
|
|
||||||
<!-- 所在港(nowPort,与平板端一致;新增默认 HFE,由 ViewModel 手机开关预填) -->
|
<!-- 所在港(nowPort,新增默认 HFE) -->
|
||||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -222,7 +210,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||||
hint='@{"请选择来源"}'
|
hint='@{"请选择来源"}'
|
||||||
list="@{viewModel.sourceEditList}"
|
list="@{viewModel.sourceList}"
|
||||||
title='@{"来源"}'
|
title='@{"来源"}'
|
||||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}"
|
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}"
|
||||||
value="@={viewModel.uldBean.source}" />
|
value="@={viewModel.uldBean.source}" />
|
||||||
@@ -233,7 +221,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||||
hint='@{"请输入入库时间"}'
|
hint='@{"请输入入库时间"}'
|
||||||
required="@{viewModel.pageType == DetailsPageType.Add}"
|
|
||||||
title='@{"入库时间"}'
|
title='@{"入库时间"}'
|
||||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.DATE}"
|
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.DATE}"
|
||||||
value="@={viewModel.uldBean.inStoreTime}" />
|
value="@={viewModel.uldBean.inStoreTime}" />
|
||||||
|
|||||||
@@ -118,6 +118,34 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- ==================== 所在港 / 位置(与平板列表一致) ==================== -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text='@{"所在港:" + (bean.nowPort.empty ? "-" : bean.nowPort)}'
|
||||||
|
android:textColor="@color/phone_text_body"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text='@{"位置:" + (bean.nowPosition.empty ? "-" : bean.nowPosition)}'
|
||||||
|
android:textColor="@color/phone_text_body"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- ==================== 第三行:入库时间 ==================== -->
|
<!-- ==================== 第三行:入库时间 ==================== -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
219
documents/双端合入主干交接文档.md
Normal file
219
documents/双端合入主干交接文档.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# 平板 + 5.5 寸手机合入主干后的交接
|
||||||
|
|
||||||
|
> 日期:2026-10-11 | 基线提交:`origin/main @ 131eb03` | 在线版:https://claude.ai/artifact/N8cWdTcMAzk9N86NSPevTC
|
||||||
|
>
|
||||||
|
> 本文描述合并当天的状态。分支、版本号等信息如有变化,以仓库为准。
|
||||||
|
|
||||||
|
## 接手前先知道这几件事
|
||||||
|
|
||||||
|
1. **只有一条开发分支:`main`。** 手机适配分支 `feature/phone-ui-adaptation` 已于 2026-10-11 合入,平板和 5.5 寸手机以后都在 `main` 上一起开发。
|
||||||
|
2. **业务以平板为准,手机只做界面。** 两端共用 ViewModel、接口、表单项、筛选入参和默认值;差异只能出现在布局(View)层。
|
||||||
|
3. **同一个页面有两份同名布局**:`res/layout-sw600dp/` 是平板,`res/layout/` 是手机。改平板页面时要同时检查手机布局和这个页面的所有弹框。
|
||||||
|
4. **有两件事等人拍板**:ULD 保存可能报 500(问题清单 #36),以及手机独有的几个界面功能留不留(见第 6 节)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 仓库与分支
|
||||||
|
|
||||||
|
| 分支 / 位置 | 提交 | 状态 | 建议 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `origin/main` | 131eb03 | 唯一开发分支 | 以后所有开发从这里拉分支、合回这里。 |
|
||||||
|
| 本地 `main`(主 checkout `~/Development/Projects/aerologic-app`) | 871fa4c | 落后 30 个提交 | 合并是在另一个 worktree 里推送的,主 checkout 还没同步。到那个目录执行 `git pull --ff-only`。 |
|
||||||
|
| `feature/phone-ui-adaptation`(本地 + 远端) | 42647c2 | 已完整合入 | 不要再往上提交。确认无人使用后可以删除(删除远端分支前先跟团队确认)。 |
|
||||||
|
| `worktree/phone-adaptation`(仅本地) | 42647c2 | 已完整合入 | 可删除。 |
|
||||||
|
| `merge/phone-into-main`(仅本地,worktree `~/.herdr/worktrees/aerologic-app/worktree-phone-adaptation` 当前检出) | 131eb03 | 与 main 相同 | 合并时用的临时分支。可以 `git worktree remove` 这个 worktree 后删除分支。 |
|
||||||
|
|
||||||
|
合并提交 `131eb03` 的第一父提交是原 main(`871fa4c`),第二父提交是手机分支(`42647c2`),所以 `git log --first-parent main` 仍然是平板的历史。两条线的分叉点是 `9c4588e`:分叉后 main 有 58 个平板提交,手机分支有 29 个提交,合并时共 45 个文件冲突。
|
||||||
|
|
||||||
|
## 2. 平板与手机的开发状态
|
||||||
|
|
||||||
|
**平板(10 寸横屏)**
|
||||||
|
|
||||||
|
- 功能完整,即合并前 main 上的全部功能。
|
||||||
|
- 合并没有改平板的业务逻辑。冲突的平板布局用三方合并放进了 `layout-sw600dp/`,内容就是 main 最新版。
|
||||||
|
- 已有手机变体的页面:20 个 Activity 布局 + 11 个弹框布局在 `layout-sw600dp/`。
|
||||||
|
- 其余平板页面(舱单、理货、组装、航班管理等)只有一份布局,放在 `res/layout/`,没有手机版。
|
||||||
|
|
||||||
|
**手机(5.5 寸竖屏)**
|
||||||
|
|
||||||
|
- 设计稿 01~11 的 11 个入口全部完成,并已按 main 的新平板功能补齐(见第 3 节)。
|
||||||
|
- 手机首页是 `module_p` 的 `PDAEnterActivity`,菜单是手写清单(`GjViewModel` / `GnViewModel`),按登录权限串过滤。
|
||||||
|
- 设计参考:`documents/5.5寸手持端设计文件/`,技术方案 `documents/5.5寸手持端适配技术方案.md`。
|
||||||
|
|
||||||
|
### 手机已适配页面
|
||||||
|
|
||||||
|
| 首页入口 | 页面(Activity) | 模块 | 说明 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 国际 · 出港移库 | `IntExpMoveActivity`、`IntExpMoveEditActivity` | gjc | 详情 / 编辑与平板共用同一个 Activity,手机布局为「运单详情」样式。 |
|
||||||
|
| 国际 · 出库交接 | `IntExpOutHandoverActivity`、`IntExpOutHandoverDetailActivity` | gjc | 底部栏带「允许装机」「配运」次按钮。 |
|
||||||
|
| 国际 · 出港计重 | `GjcWeighingListActivity`、`GjcWeighingStartActivity`、`GjcWeighingRecordListActivity`、`GjcWeighingRecordDetailsActivity` | gjc | 另有手机专属 `GjcWeighingWbDetailActivity`(运单详情)。 |
|
||||||
|
| 国际 · 出港运抵 | `IntExpArriveActivity` | gjc | 手机专属 `IntExpArriveSubOrderActivity`(分单明细)、`IntExpArriveReceiptActivity`(分单回执);主单回执用两端共用的 `CustomsResponseActivity`。 |
|
||||||
|
| 国际 · 出港查询 | `GjcQueryActivity`、`GjcQueryDetailsActivity`、`LogDetailActivity`(运单追踪) | gjc / app | |
|
||||||
|
| 国际 · 出港仓库 | `IntExpStorageUseActivity` | gjc | 平板的「勾选 + 底部批量」在手机上改为卡片四按钮:清仓 / 修改库位 / 出库 / 入库。 |
|
||||||
|
| 国际 · 进港仓库 | `IntImpStorageUseActivity`、`IntImpQueryDetailsActivity` | gjj | 接口前缀是 `IntImpStorage`,不是 `IntImpStorageUse`。 |
|
||||||
|
| 国际 · 进港查询 | `IntImpQueryActivity` | gjj | |
|
||||||
|
| 国际 · 提取出库 | `IntImpPickUpDLVActivity` | gjj | 手机专属 `IntImpPickUpDetailActivity`。 |
|
||||||
|
| 国际 · ULD 管理 | `UldListActivity`、`UldEditActivity` | app | 一套编辑布局覆盖新增 / 修改 / 详情。 |
|
||||||
|
| 国内 · 进港移库 | `GnjMoveStashListActivity` | app | 业务归属国内进港移库(DomImpMove)。手机专属 `GnjMoveStashDetailActivity`、`GnjMoveStashPhotoActivity`。 |
|
||||||
|
|
||||||
|
手机首页还有一批旧的 PDA 竖屏入口(国际:普货收运、板箱组装、板箱过磅、进港理货;国内:出港收运、出港复磅、出港存放、进港卸机、货物转运、转运记录、事故签证、单证交接)。它们是 `module_p` 原有页面,不在这次设计稿范围内,合并没有改动,也没有验证。
|
||||||
|
|
||||||
|
## 3. 这次合并做了什么
|
||||||
|
|
||||||
|
### 冲突处理原则
|
||||||
|
|
||||||
|
- **布局**:平板布局取 main 最新版,三方合并进 `layout-sw600dp/`,没有冲突残留。手机布局保留原样,再逐页补上 main 新增的业务字段和操作。
|
||||||
|
- **Bean / ViewModel**:一律取平板逻辑。手机只保留它额外需要的展示字段(如 `opUserDisplay`、统计字段)。
|
||||||
|
- **Manifest**:取手机分支版本(屏幕方向统一为 `unspecified`),再补上 main 新增的 Activity。
|
||||||
|
|
||||||
|
### main 的平板功能同步到手机
|
||||||
|
|
||||||
|
| 页面 | 同步到手机的内容 |
|
||||||
|
|---|---|
|
||||||
|
| 出港运抵 | 卡片显示海关放行、运单类型;筛选加分单号;回执改走平板同一接口和回执页;长按菜单加「删除申报」(手机底部弹框)。 |
|
||||||
|
| 出库交接 | 卡片显示装机状态;底部栏加「允许装机」「配运」;配运弹框做了手机变体。 |
|
||||||
|
| 出港移库 | 详情和编辑改用平板的 `IntExpMoveEditActivity`,删除手机专属 `IntExpMoveDetailActivity`;卡片加「编辑」;筛选加卸货站。 |
|
||||||
|
| 出港 / 进港仓库 | 清仓弹框加备注,卡片显示清仓备注;入库、出库弹框可填件数和重量(出库弹框新做了手机变体);进港卡片件数取 `pcLabel`。 |
|
||||||
|
| 开始计重 | ViewModel 整体取平板版本:加运单号、地磅重量、平板车、托盘信息,运抵重量按平板公式自动计算;航班日期 / 航班号 / 航程可编辑。 |
|
||||||
|
| 计重 / 计重记录 | 计重列表加「新增」按钮;计重记录加航班号筛选,卡片加「运抵申报」入口。 |
|
||||||
|
| 计重明细 | 重量与托盘自重按平板逻辑联动(和恒定),同时保留手机的输入原文处理(避免光标跳动)。 |
|
||||||
|
| 进港查询 | 筛选加出库状态。 |
|
||||||
|
| ULD | 采用平板的来源列表与 HFE 默认值;卡片显示所在港 / 位置。 |
|
||||||
|
|
||||||
|
### 手机按平板收敛的地方(使用上能感觉到)
|
||||||
|
|
||||||
|
- 删除了平板不传的查询入参:`likeNo`(8 位运单号模糊查询)、进港移库的航班日期 / 航班号 / 特码、出港移库的特码。
|
||||||
|
- 恢复平板默认查询值:计重、计重记录、提取出库等页面默认查当天,首次打开常常是空列表,属于预期行为。
|
||||||
|
- 补齐平板有而手机缺的筛选项:运抵分单号、进港查询出库状态、计重记录航班号、进港移库承运人、出港移库卸货站。
|
||||||
|
- ULD 编辑去掉平板没有的「进港航班号」和手机额外加的必填校验(影响见 #36)。
|
||||||
|
|
||||||
|
## 4. 以后怎么开发
|
||||||
|
|
||||||
|
项目根目录的 `CLAUDE.md`「5.5 寸手持端双形态适配规范」一节是完整规范,`.claude/skills/phone-adapt/` 是手机适配流程。下面是最常用的几条。
|
||||||
|
|
||||||
|
### 布局双变体的硬性约束
|
||||||
|
|
||||||
|
- 两个变体**文件同名**:平板 `layout-sw600dp/xxx.xml`,手机 `layout/xxx.xml`。禁止用 `xxx_phone.xml` 加代码判断,那样会生成另一个 Binding 类,进页面就 `ClassCastException`。
|
||||||
|
- 两个变体的 `<variable>` 名称和类型必须一致。
|
||||||
|
- Kotlin 里引用的控件 id 两边都要有;只有一边有的 id 在 Binding 里是可空的,代码里要写 `binding.xxx?.`。
|
||||||
|
- 弹框同样做双变体,并在构造参数里切换位置:`BaseDialogModel(if (DeviceUtil.isPhone()) DIALOG_TYPE_BOTTOM else DIALOG_TYPE_CENTER)`。
|
||||||
|
- Manifest 新 Activity 的 `screenOrientation` 写 `unspecified`,由 `BaseActivity` 按设备锁方向。
|
||||||
|
|
||||||
|
### 平板改了一个已适配页面,标准步骤
|
||||||
|
|
||||||
|
1. 改平板布局(`layout-sw600dp/`)和 ViewModel。
|
||||||
|
2. 打开同名手机布局(`layout/`),补上同样的字段、筛选项和操作入口。平板的批量按钮在手机上可以改成卡片按钮或 `PhoneBottomBar` 的次按钮(`subAction1Text` / `subAction2Text`)。
|
||||||
|
3. 检查这个页面用到的每一个 DialogModel,新弹框要做手机变体。
|
||||||
|
4. 手机 Tab 角标、计数这类只有手机用的额外请求,放在 `phoneExtrasEnabled` 一类的开关后面,不能改变平板的请求。
|
||||||
|
5. 两台模拟器各走一遍,再编译 `./gradlew assembleDebug`。
|
||||||
|
|
||||||
|
### 手机端不能做的事
|
||||||
|
|
||||||
|
- 不能新增平板没有的查询入参(后端口径:手机筛选一律与平板一致,后端不认识的参数会被静默忽略)。
|
||||||
|
- 不能改写平板的默认查询值(如默认当天)。
|
||||||
|
- 不能少平板的表单项或校验,也不能多加平板没有的校验。
|
||||||
|
|
||||||
|
### 新做一个手机页面
|
||||||
|
|
||||||
|
按 `CLAUDE.md`「手机版页面改造步骤」:`git mv` 平板布局到 `layout-sw600dp/` → 写同名手机布局 → 优先复用 `module_base/.../ui/weight/phone/` 下的组件(`PhoneSearchBar`、`PhoneFormRow`、`PhoneBottomBar` 等)→ 到 `GjViewModel` / `GnViewModel` 加首页菜单入口(最容易忘,忘了在手机上就进不去)。
|
||||||
|
|
||||||
|
## 5. 已验证与未验证
|
||||||
|
|
||||||
|
验证环境:手机模拟器 `AeroLogic_Phone`(720×1440,API 31,sw=394dp),平板模拟器 `AeroLogic_Tablet`(1280×800,sw=800dp),连内网测试服务。
|
||||||
|
|
||||||
|
| 项目 | 结果 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| Debug 编译 | ✅ 通过 | 合并后 `assembleDebug` 成功。 |
|
||||||
|
| 手机 11 个入口主流程 | ✅ 已验证 | 列表、Tab、筛选、详情、弹框打开,无崩溃。 |
|
||||||
|
| 平板回归(已适配的页面) | ✅ 已验证 | 主要页面打开、筛选、弹框,无崩溃,布局为 main 最新版。 |
|
||||||
|
| 开始计重自动计算 | ✅ 已验证 | 地磅 500、托盘 2 个 × 18 → 运抵重量 464,与平板公式一致。新增模式头部不再显示 "null kg"。 |
|
||||||
|
| 手机新弹框(出库 / 入库 / 清仓 / 配运 / 删除申报) | ⚠️ 只打开过 | 样式和字段都确认了,没有点提交,以免改动测试数据。 |
|
||||||
|
| 所有写操作(申报、出入库、清仓、保存、移库、拍照上传) | ⚠️ 未提交验证 | 合并后没有真实提交过。建议用测试数据逐个点一次提交,平板和手机都要。 |
|
||||||
|
| 海关回执页有内容时的显示 | ❌ 未验证 | 测试数据里没有带回执的运单。 |
|
||||||
|
| ULD 管理、国内进港移库 | ❌ 只编译过 | 合并后的改动没在模拟器上点过。 |
|
||||||
|
| 手机首页旧 PDA 入口 | ❌ 未验证 | 不在本次范围。 |
|
||||||
|
| Release 包、真机(交付手持机 / 平板) | ❌ 未验证 | 只在模拟器上跑过 Debug 包。 |
|
||||||
|
|
||||||
|
## 6. 已知问题与待决事项
|
||||||
|
|
||||||
|
### 需要决定
|
||||||
|
|
||||||
|
- **ULD 保存可能报 500(问题清单 #36)。** 后端 `saveUld` 在缺少最大装载容积 / 最大载重时返回 500。之前只有手机做了必填拦截;按「以平板为准」去掉之后,两端都可能报错。可选做法:让后端修,或者在平板加同样的必填校验(再同步到手机)。
|
||||||
|
- **手机独有的界面功能目前保留。** 它们只在手机上发请求,不影响平板,所以合并时留下了:
|
||||||
|
- 各列表的「待 / 已」Tab,以及 Tab 角标额外发的统计请求;
|
||||||
|
- 进港移库「已移库」Tab(调用平板不用的 `searchMoved` 接口);
|
||||||
|
- 出港运抵分单明细页、分单回执页;ULD 状态显示为「可用 / 维修」;ULD 入库时间保存时补 `00:00:00`。
|
||||||
|
|
||||||
|
如果要求两端严格一致,需要逐项删除。
|
||||||
|
|
||||||
|
### 对用户的行为变化(属于预期)
|
||||||
|
|
||||||
|
- 手机列表默认查当天,首次打开常常为空,要在筛选里清掉日期。
|
||||||
|
- 手机输入 8 位运单号不再能模糊查到(去掉了 `likeNo`),和平板一致。
|
||||||
|
|
||||||
|
### 后端缺口(问题清单 #35~#41,待后端处理)
|
||||||
|
|
||||||
|
- ULD 列表按 uld 搜索返回 500;`saveUld` 缺 maxVolume / maxWeight 返回 500(#36)。
|
||||||
|
- `IntImpStorage/pageQuery` 列表过滤全部不生效。
|
||||||
|
- 出港仓库列表与统计的口径不一致。
|
||||||
|
- 已移库数据没有 `mawbId`,无法补传照片。
|
||||||
|
|
||||||
|
完整内容和每一项的后端答复在 `documents/后端接口对接问题清单.xlsx`。这份表是和后端对接的正式文档,新发现的缺口要继续记在里面。
|
||||||
|
|
||||||
|
### 其他待办
|
||||||
|
|
||||||
|
- `PhoneSearchBar` 还没接 AutoQuery 联想(`AutoQueryManager` 只接了平板组件)。
|
||||||
|
|
||||||
|
## 7. 容易踩的坑
|
||||||
|
|
||||||
|
1. **API 文档的 token 提交进了仓库。** `.mcp.json` 里写着 api-doc MCP 的 `Authorization: Bearer …`,并且已被 git 跟踪。建议轮换这个 token,改为从环境变量或本地未跟踪的配置读取。
|
||||||
|
2. **换行符混用,没有 `.gitattributes`。** main 原有文件多为 CRLF,手机分支新建的文件是 LF。编辑时保持文件原有换行符,否则会出现整文件 diff,合并和 review 都会很难看。
|
||||||
|
3. **没有手机变体的平板页面,在手机上会直接显示平板布局。** 给手机首页加入口前,必须先做手机布局。
|
||||||
|
4. **后端会静默忽略不认识的参数。** 猜错参数名会得出「后端不支持」的误判。入参出参一律用 api-doc MCP 核对,流程见 `.claude/skills/phone-adapt/references/api-doc.md`。
|
||||||
|
5. **XML 里 `??` 兜底不生效。** 网络层会把 JSON null 转成空串,`??` 只判断 null。要在 Bean 上写计算属性(如 `agentDisplay`、`opUserDisplay`)。
|
||||||
|
6. **RecyclerView 里给 item 设 `GONE` 不会收起高度。** Tab 过滤要在数据层过滤后整体 `refresh`。
|
||||||
|
7. **Intent 传递的 Bean 里 `@Transient` 字段会变成 null**(`checked`、`showMore` 等)。接收方要 `.map { it.copy() }`。
|
||||||
|
8. **同页多个手机复合组件会串状态。** 新写同类组件要覆写 `dispatchSaveInstanceState` / `dispatchRestoreInstanceState`(见 CLAUDE.md)。
|
||||||
|
|
||||||
|
第 5~8 条在 CLAUDE.md「常见编译错误速查」里都有详细说明和修复方式。
|
||||||
|
|
||||||
|
## 8. 环境与工具
|
||||||
|
|
||||||
|
- **工具链**:Gradle 用 Java 17 运行(`mise.toml`),编译目标是 Java 1.8;当前版本 1.8.7(versionCode 87)。
|
||||||
|
- **内网**:测试服务器在内网,模拟器要在内网环境下才能拉到数据,否则提示「接口访问失败」。测试账号向项目负责人索取(不写在文档里)。
|
||||||
|
- **登录不保留**:冷启动后要重新登录。
|
||||||
|
- **Debug 直达页面**:`app/src/debug/AndroidManifest.xml` 把适配页设为 exported,可以直接拉起(只在 Debug 包生效)。
|
||||||
|
- **平板模拟器没有物理按键**,`adb shell monkey` 拉起会失败,改用 `am start`。
|
||||||
|
- **手机模拟器**上软键盘弹出时按返回键,有时会被吞掉并退出到桌面,不是崩溃。
|
||||||
|
- **mobile-mcp** 偶尔识别不到 `emulator-5554`,可以直接用 adb。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启动模拟器(模拟器可执行文件在 Homebrew 的 android-commandlinetools 下)
|
||||||
|
/opt/homebrew/share/android-commandlinetools/emulator/emulator -avd AeroLogic_Phone &
|
||||||
|
/opt/homebrew/share/android-commandlinetools/emulator/emulator -avd AeroLogic_Tablet &
|
||||||
|
|
||||||
|
# 编译安装
|
||||||
|
./gradlew assembleDebug
|
||||||
|
adb -s emulator-5554 install -r app/build/outputs/apk/debug/*.apk
|
||||||
|
|
||||||
|
# 拉起登录页(平板没有物理键时用这个)
|
||||||
|
adb -s emulator-5556 shell am start -n com.lukouguoji.aerologic/com.lukouguoji.module_base.LoginActivity
|
||||||
|
|
||||||
|
# 确认设备形态识别
|
||||||
|
adb -s emulator-5554 logcat -d -s BaseActivity:D # PHONE(sw=394dp) / TABLET(sw=800dp)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 关键文件索引
|
||||||
|
|
||||||
|
| 文件 | 用途 |
|
||||||
|
|---|---|
|
||||||
|
| `CLAUDE.md` | 项目规范总入口,包括双形态适配规范、分支约定、常见错误速查。 |
|
||||||
|
| `CHANGELOG.md` | [Unreleased] 下「手机适配分支合入主干 · 2026-10-11」是本次合并的完整记录。 |
|
||||||
|
| `documents/后端接口对接问题清单.xlsx` | 与后端对接的正式问题清单(#1~#41)。 |
|
||||||
|
| `documents/5.5寸手持端设计文件/` | 手机设计稿(HTML)。 |
|
||||||
|
| `.claude/skills/phone-adapt/` | 手机适配流程,含 api-doc、组件、平板安全、验证等参考。 |
|
||||||
|
| `module_base/.../util/DeviceUtil.kt` | 手机 / 平板判定(sw ≥ 600dp 为平板)。 |
|
||||||
|
| `module_base/.../ui/weight/phone/` | 手机公共组件,适配器在 `PhoneWidgetKtx.kt`。 |
|
||||||
|
| `module_base/.../MyApplication.kt` | AutoSize 设计基准(手机竖屏 390×844,平板横屏 1152×720)。 |
|
||||||
|
| `module_p/.../ui/enter/gj/GjViewModel.kt`、`gn/GnViewModel.kt` | 手机首页国际 / 国内菜单清单。 |
|
||||||
|
| `app/.../HomeFragment.kt` | 平板首页菜单,手机入口的权限串和路由要以它为准。 |
|
||||||
@@ -2,6 +2,7 @@ package com.lukouguoji.module_base.adapter
|
|||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
|
import android.widget.LinearLayout
|
||||||
import androidx.databinding.BindingAdapter
|
import androidx.databinding.BindingAdapter
|
||||||
import com.lukouguoji.module_base.interfaces.IOnFocusChangeListener
|
import com.lukouguoji.module_base.interfaces.IOnFocusChangeListener
|
||||||
|
|
||||||
@@ -27,6 +28,18 @@ fun visible(view: View, visible: Any?) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态设置 LinearLayout 子 View 的 layout_weight(如按状态切换列宽)
|
||||||
|
*/
|
||||||
|
@BindingAdapter("layoutWeight")
|
||||||
|
fun setLayoutWeight(view: View, weight: Float) {
|
||||||
|
val lp = view.layoutParams as? LinearLayout.LayoutParams ?: return
|
||||||
|
if (lp.weight != weight) {
|
||||||
|
lp.weight = weight
|
||||||
|
view.layoutParams = lp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@BindingAdapter("setFocusChangedListener")
|
@BindingAdapter("setFocusChangedListener")
|
||||||
fun setOnFocusChangeListener(et: EditText, listener: IOnFocusChangeListener) {
|
fun setOnFocusChangeListener(et: EditText, listener: IOnFocusChangeListener) {
|
||||||
et.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus ->
|
et.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus ->
|
||||||
|
|||||||
@@ -19,6 +19,27 @@ class AssembleWaybillBean {
|
|||||||
var originalPieces: String = "" // 原始运单件数
|
var originalPieces: String = "" // 原始运单件数
|
||||||
var originalWeight: String = "" // 原始运单重量
|
var originalWeight: String = "" // 原始运单重量
|
||||||
|
|
||||||
|
// ========== 剩余/总数显示(开始组装页首次装货成功后切换) ==========
|
||||||
|
var checkInPieces: Long = 0 // 已组装件数(接口 checkInPc)
|
||||||
|
var checkInWeight: Double = 0.0 // 已组装重量(接口 checkInWeight)
|
||||||
|
var showRemaining: Boolean = false // 是否以“剩余/总数”形式显示
|
||||||
|
|
||||||
|
/** 件数列显示:首次装货成功前为总件数,之后为“剩余件数/总件数”(剩余 = pc − checkInPc) */
|
||||||
|
val piecesDisplay: String
|
||||||
|
get() {
|
||||||
|
if (!showRemaining) return pieces
|
||||||
|
val total = pieces.toLongOrNull() ?: return pieces
|
||||||
|
return "${total - checkInPieces}/$pieces"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重量列显示:首次装货成功前为总重量,之后为“剩余重量/总重量”(剩余 = weight − checkInWeight) */
|
||||||
|
val weightDisplay: String
|
||||||
|
get() {
|
||||||
|
if (!showRemaining) return weight
|
||||||
|
val total = weight.toDoubleOrNull() ?: return weight
|
||||||
|
return String.format("%.1f", total - checkInWeight) + "/" + weight
|
||||||
|
}
|
||||||
|
|
||||||
val fLightInfo: String
|
val fLightInfo: String
|
||||||
get() = "$fno/${fdate.replace("-", "")}"
|
get() = "$fno/${fdate.replace("-", "")}"
|
||||||
|
|
||||||
|
|||||||
@@ -234,7 +234,10 @@ class FlightBean : ICheck {
|
|||||||
return when (countryType) {
|
return when (countryType) {
|
||||||
"0" -> "国内"
|
"0" -> "国内"
|
||||||
"1" -> "国际"
|
"1" -> "国际"
|
||||||
else -> ""
|
"2" -> "地区"
|
||||||
|
"3" -> "混合"
|
||||||
|
// 部分接口直接返回中文名,原样显示
|
||||||
|
else -> countryType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.lukouguoji.module_base.bean
|
|||||||
import androidx.databinding.BaseObservable
|
import androidx.databinding.BaseObservable
|
||||||
import androidx.databinding.Bindable
|
import androidx.databinding.Bindable
|
||||||
import androidx.databinding.library.baseAdapters.BR
|
import androidx.databinding.library.baseAdapters.BR
|
||||||
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港计重记录明细Bean
|
* 国际出港计重记录明细Bean
|
||||||
@@ -22,9 +23,9 @@ data class GjcCheckInRecord(
|
|||||||
var weight: Double = 0.0, // 运抵重量
|
var weight: Double = 0.0, // 运抵重量
|
||||||
var volume: Double = 0.0, // 运抵体积
|
var volume: Double = 0.0, // 运抵体积
|
||||||
var whId: Long = 0, // GJC_WAREHOUSE.ID
|
var whId: Long = 0, // GJC_WAREHOUSE.ID
|
||||||
var carWeight: String = "", // 托盘车重(平板托盘车号反查值)
|
var carWeight: String = "", // 托盘车重
|
||||||
var trayNumber: Int = 0, // 托盘数量
|
var trayNumber: Int = 0, // 托盘数量
|
||||||
var trayTotalWeight: Double = 0.0, // 托盘重量(托盘数量 × 托盘自重,与平板端一致)
|
var trayTotalWeight: Double = 0.0, // 托盘重量(托盘自重)
|
||||||
var opUserName: String = "" // 计重人姓名(opId 关联查询)
|
var opUserName: String = "" // 计重人姓名(opId 关联查询)
|
||||||
) : BaseObservable() {
|
) : BaseObservable() {
|
||||||
|
|
||||||
@@ -48,37 +49,76 @@ data class GjcCheckInRecord(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重量的字符串表示(用于双向绑定)
|
// 重量 / 托盘自重编辑过程中的原始输入串(保持回显与输入一致,避免光标跳位)
|
||||||
@get:Bindable
|
// 不作为构造参数,data class copy 备份/恢复时自动丢弃,回退到数值显示
|
||||||
var weightStr: String
|
// @Transient:仅界面内部状态,不参与 Gson 序列化提交
|
||||||
get() = if (weight == 0.0) "" else weight.toString()
|
@Transient
|
||||||
set(value) {
|
private var weightInput: String? = null
|
||||||
val newWeight = value.toDoubleOrNull() ?: 0.0
|
|
||||||
if (weight != newWeight) {
|
|
||||||
weight = newWeight
|
|
||||||
notifyPropertyChanged(BR.weightStr)
|
|
||||||
onDataChanged?.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 托盘重量输入原文(手机「修改记录」覆层)。保留原文而非由 Double 反推:
|
|
||||||
* 输入 "1" 时若 getter 返回 "1.0",输入框会被重设、光标回到开头,再输 "8" 就成了 "81"(实机踩过)。
|
|
||||||
* transient:不参与 Gson 提交序列化。
|
|
||||||
*/
|
|
||||||
@Transient
|
@Transient
|
||||||
private var trayTotalWeightInput: String? = null
|
private var trayTotalWeightInput: String? = null
|
||||||
|
|
||||||
// 托盘重量的字符串表示(用于双向绑定,5.5寸手机版「修改记录」覆层)
|
// 重量的字符串表示(用于双向绑定)
|
||||||
|
// 业务规则:重量 + 托盘自重 恒定(等于接口返回初始值之和),修改一方另一方反向同步变动
|
||||||
|
// 校验:任一方不能小于 0,非法输入直接拒绝并回退显示
|
||||||
|
@get:Bindable
|
||||||
|
var weightStr: String
|
||||||
|
get() = weightInput ?: if (weight == 0.0) "" else weight.toString()
|
||||||
|
set(value) {
|
||||||
|
val newWeight = value.toDoubleOrNull() ?: 0.0
|
||||||
|
val newTrayWeight = round2(weight + trayTotalWeight - newWeight)
|
||||||
|
when {
|
||||||
|
newWeight < 0 -> {
|
||||||
|
showToast("重量不能小于 0")
|
||||||
|
notifyPropertyChanged(BR.weightStr)
|
||||||
|
}
|
||||||
|
newTrayWeight < 0 -> {
|
||||||
|
showToast("重量不能超过 ${round2(weight + trayTotalWeight)},否则托盘自重将小于 0")
|
||||||
|
notifyPropertyChanged(BR.weightStr)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
weightInput = value
|
||||||
|
if (weight != newWeight) {
|
||||||
|
weight = newWeight
|
||||||
|
trayTotalWeight = newTrayWeight
|
||||||
|
trayTotalWeightInput = null
|
||||||
|
notifyPropertyChanged(BR.weightStr)
|
||||||
|
notifyPropertyChanged(BR.trayTotalWeightStr)
|
||||||
|
onDataChanged?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 托盘自重的字符串表示(用于双向绑定),联动规则同上
|
||||||
@get:Bindable
|
@get:Bindable
|
||||||
var trayTotalWeightStr: String
|
var trayTotalWeightStr: String
|
||||||
get() = trayTotalWeightInput ?: if (trayTotalWeight == 0.0) "" else trayTotalWeight.toString()
|
get() = trayTotalWeightInput ?: if (trayTotalWeight == 0.0) "" else trayTotalWeight.toString()
|
||||||
set(value) {
|
set(value) {
|
||||||
if (trayTotalWeightStr != value) {
|
val newTrayWeight = value.toDoubleOrNull() ?: 0.0
|
||||||
trayTotalWeightInput = value
|
val newWeight = round2(weight + trayTotalWeight - newTrayWeight)
|
||||||
trayTotalWeight = value.toDoubleOrNull() ?: 0.0
|
when {
|
||||||
notifyPropertyChanged(BR.trayTotalWeightStr)
|
newTrayWeight < 0 -> {
|
||||||
onDataChanged?.invoke()
|
showToast("托盘自重不能小于 0")
|
||||||
|
notifyPropertyChanged(BR.trayTotalWeightStr)
|
||||||
|
}
|
||||||
|
newWeight < 0 -> {
|
||||||
|
showToast("托盘自重不能超过 ${round2(weight + trayTotalWeight)},否则重量将小于 0")
|
||||||
|
notifyPropertyChanged(BR.trayTotalWeightStr)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
trayTotalWeightInput = value
|
||||||
|
if (trayTotalWeight != newTrayWeight) {
|
||||||
|
trayTotalWeight = newTrayWeight
|
||||||
|
weight = newWeight
|
||||||
|
weightInput = null
|
||||||
|
notifyPropertyChanged(BR.trayTotalWeightStr)
|
||||||
|
notifyPropertyChanged(BR.weightStr)
|
||||||
|
onDataChanged?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun round2(v: Double): Double = Math.round(v * 100) / 100.0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.lukouguoji.module_base.bean
|
package com.lukouguoji.module_base.bean
|
||||||
|
|
||||||
import androidx.databinding.ObservableBoolean
|
import androidx.databinding.ObservableBoolean
|
||||||
|
import com.lukouguoji.module_base.ktx.toPlainText
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
@@ -45,6 +46,7 @@ data class GjcMaWb(
|
|||||||
|
|
||||||
// ==================== 货物信息 ====================
|
// ==================== 货物信息 ====================
|
||||||
var pc: Long? = null, // 预配件数
|
var pc: Long? = null, // 预配件数
|
||||||
|
var pcLabel: String? = null, // 件数展示文本(国际进港仓库分页返回,如 0/8、44)
|
||||||
var weight: Double? = null, // 预配重量(KG)
|
var weight: Double? = null, // 预配重量(KG)
|
||||||
var volume: Double? = null, // 预配体积(CBM)
|
var volume: Double? = null, // 预配体积(CBM)
|
||||||
var goods: String? = null, // 品名(英)
|
var goods: String? = null, // 品名(英)
|
||||||
@@ -58,6 +60,8 @@ data class GjcMaWb(
|
|||||||
var arriveWeight: Double? = null, // 运抵重量
|
var arriveWeight: Double? = null, // 运抵重量
|
||||||
var arriveVolume: Double? = null, // 运抵体积
|
var arriveVolume: Double? = null, // 运抵体积
|
||||||
var arriveFlag: String? = null, // 运抵状态(0:正常运抵,1:提前运抵)
|
var arriveFlag: String? = null, // 运抵状态(0:正常运抵,1:提前运抵)
|
||||||
|
var trayNumber: Int? = null, // 托盘数量
|
||||||
|
var trayTotalWeight: Double? = null, // 托盘重量(托盘数量 × 托盘自重)
|
||||||
|
|
||||||
// ==================== 特码与收货人 ====================
|
// ==================== 特码与收货人 ====================
|
||||||
var spCode: String? = null, // 特码
|
var spCode: String? = null, // 特码
|
||||||
@@ -76,15 +80,16 @@ data class GjcMaWb(
|
|||||||
var carNumber: String? = null, // 车牌号
|
var carNumber: String? = null, // 车牌号
|
||||||
var passageWay: String? = null, // 通道号
|
var passageWay: String? = null, // 通道号
|
||||||
var passageWayId: String? = null,
|
var passageWayId: String? = null,
|
||||||
var trayNumber: Int? = null, // 托盘数量(splitCheckIn / completeCheckIn 入参,与平板端一致)
|
|
||||||
var trayTotalWeight: Double? = null, // 托盘重量 = 托盘数量 × 托盘自重
|
|
||||||
|
|
||||||
// ==================== 状态信息 ====================
|
// ==================== 状态信息 ====================
|
||||||
var checkIn: String? = null, // 收运状态。0:待收运,1:已收运,2:收运中
|
var checkIn: String? = null, // 收运状态。0:待收运,1:已收运,2:收运中
|
||||||
var declareStatus: String? = null, // 申报状态
|
var declareStatus: String? = null, // 申报状态
|
||||||
|
var cmdStatus: String? = null, // 海关放行(命令状态)
|
||||||
var reviewStatus: String? = null, // 审核状态(0:未审核;1:通过;2:退回)
|
var reviewStatus: String? = null, // 审核状态(0:未审核;1:通过;2:退回)
|
||||||
var tranFlag: String? = null, // 转运标志
|
var tranFlag: String? = null, // 转运标志
|
||||||
var clearNormal: String? = null, // 清仓正常(0:否,1:是)
|
var clearNormal: String? = null, // 清仓正常(0:否,1:是)
|
||||||
|
var clearRemark: String? = null, // 清仓备注
|
||||||
|
var allTally: String? = null, // 运单理货申报全部通过(0:否;1:是),为 1 时才允许全选该主单下的分单
|
||||||
|
|
||||||
// ==================== 操作信息 ====================
|
// ==================== 操作信息 ====================
|
||||||
var opDate: String? = null, // 操作时间(入库时间)
|
var opDate: String? = null, // 操作时间(入库时间)
|
||||||
@@ -94,6 +99,9 @@ data class GjcMaWb(
|
|||||||
|
|
||||||
// ==================== 备注与其他 ====================
|
// ==================== 备注与其他 ====================
|
||||||
var remark: String? = null, // 备注
|
var remark: String? = null, // 备注
|
||||||
|
var pic: String? = null, // 交接图片缩略图路径(逗号分隔)
|
||||||
|
var originalPic: String? = null, // 交接图片原图路径(逗号分隔)
|
||||||
|
var picNumber: String? = null, // 交接图片数量
|
||||||
var ffmMemo: String? = null, // ffm备注
|
var ffmMemo: String? = null, // ffm备注
|
||||||
var mftMemo: String? = null, // mft备注
|
var mftMemo: String? = null, // mft备注
|
||||||
var unNumber: String? = null, // UN编号
|
var unNumber: String? = null, // UN编号
|
||||||
@@ -131,6 +139,13 @@ data class GjcMaWb(
|
|||||||
get() = checked.get()
|
get() = checked.get()
|
||||||
set(value) = checked.set(value)
|
set(value) = checked.set(value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许全选该主单下的分单
|
||||||
|
* 主单存在分批申报(未全部申报完毕)时接口返回 allTally=0,此时不允许全选其分单
|
||||||
|
*/
|
||||||
|
val canCheckAllHaWb: Boolean
|
||||||
|
get() = allTally == "1"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 5.5寸手机版:申报状态三色标签的着色分类(01=绿/W=靛蓝/其余非空=琥珀)。
|
* 5.5寸手机版:申报状态三色标签的着色分类(01=绿/W=靛蓝/其余非空=琥珀)。
|
||||||
*/
|
*/
|
||||||
@@ -368,12 +383,21 @@ data class GjcStorageUse(
|
|||||||
var outDate: String? = null, // 出库时间
|
var outDate: String? = null, // 出库时间
|
||||||
var outOpId: String? = null, // 出库人
|
var outOpId: String? = null, // 出库人
|
||||||
var outId: String? = null, // 出库人(兼容字段)
|
var outId: String? = null, // 出库人(兼容字段)
|
||||||
var cargoStatus: String? = null // 货物状态
|
var cargoStatus: String? = null, // 货物状态
|
||||||
|
var inPc: Long? = null, // 入库件数
|
||||||
|
var inWeight: Double? = null, // 入库重量
|
||||||
|
var outPc: Long? = null, // 出库件数
|
||||||
|
var outWeight: Double? = null // 出库重量
|
||||||
) {
|
) {
|
||||||
// ==================== UI扩展字段 ====================
|
// ==================== UI扩展字段 ====================
|
||||||
@Transient
|
@Transient
|
||||||
val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态
|
val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态
|
||||||
|
|
||||||
|
val inPcText: String get() = inPc?.toString() ?: ""
|
||||||
|
val inWeightText: String get() = inWeight?.toPlainText() ?: ""
|
||||||
|
val outPcText: String get() = outPc?.toString() ?: ""
|
||||||
|
val outWeightText: String get() = outWeight?.toPlainText() ?: ""
|
||||||
|
|
||||||
// 兼容现有API的isSelected属性
|
// 兼容现有API的isSelected属性
|
||||||
var isSelected: Boolean
|
var isSelected: Boolean
|
||||||
get() = checked.get()
|
get() = checked.get()
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.lukouguoji.module_base.bean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际出港移库详情数据模型
|
||||||
|
* 对应接口:/IntExpMove/detail 返回的 data,/IntExpMove/update 的请求体
|
||||||
|
*/
|
||||||
|
data class GjcMoveDetail(
|
||||||
|
var maWbId: Long? = null, // GJC_MAWB.MAWBID
|
||||||
|
var wbNo: String? = null, // 运单号
|
||||||
|
var pc: Long? = null, // 件数
|
||||||
|
var weight: Double? = null, // 重量
|
||||||
|
var by: String? = null, // 承运人1
|
||||||
|
var awbTypeName: String? = null, // 运单类型(中文)
|
||||||
|
var range: String? = null, // 出运路径
|
||||||
|
var spCode: String? = null, // 特码
|
||||||
|
var agentCode: String? = null, // 代理人
|
||||||
|
var moveState: Int? = null, // 交接状态:0 未交接,1 已交接
|
||||||
|
var goods: String? = null, // 品名
|
||||||
|
var remark: String? = null, // 备注
|
||||||
|
var pic: String? = null, // 交接图片缩略图路径(逗号分隔)
|
||||||
|
var originalPic: String? = null, // 交接图片原图路径(逗号分隔)
|
||||||
|
var picNumber: String? = null // 交接图片数量
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* 移交状态中文(与列表项文案一致)
|
||||||
|
*/
|
||||||
|
val moveStateText: String
|
||||||
|
get() = if (moveState == 1) "已移交" else "未移交"
|
||||||
|
}
|
||||||
@@ -51,6 +51,7 @@ class GjcUldUseBean : Serializable {
|
|||||||
var uldFlag: String = "" // uld分类
|
var uldFlag: String = "" // uld分类
|
||||||
var status: String = "" // uld状态:0:正常,1:故障
|
var status: String = "" // uld状态:0:正常,1:故障
|
||||||
var dgrCode: String = "" // IMP代码
|
var dgrCode: String = "" // IMP代码
|
||||||
|
var bupFlag: String = "" // BUP选项(0:否;1:是)
|
||||||
var height: String = "" // 高度
|
var height: String = "" // 高度
|
||||||
var passageway: String = "" // 通道号
|
var passageway: String = "" // 通道号
|
||||||
var passagewayId: String = ""
|
var passagewayId: String = ""
|
||||||
@@ -65,6 +66,19 @@ class GjcUldUseBean : Serializable {
|
|||||||
|
|
||||||
var loadArea: String = "" // 组装区
|
var loadArea: String = "" // 组装区
|
||||||
var pc: Long = 0 // 组装件数
|
var pc: Long = 0 // 组装件数
|
||||||
|
var allowLoad: String = "" // 允许装机(0:不允许;1:允许)
|
||||||
|
|
||||||
|
// 是否允许装机
|
||||||
|
val isAllowLoad: Boolean
|
||||||
|
get() = allowLoad == "1"
|
||||||
|
|
||||||
|
// 装机状态文本
|
||||||
|
val allowLoadText: String
|
||||||
|
get() = when (allowLoad) {
|
||||||
|
"1" -> "允许装机"
|
||||||
|
"0" -> "不允许装机"
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
var fillWeightFlag: String = "" // 回填状态:0-未回填,1-部分回填,2-已回填
|
var fillWeightFlag: String = "" // 回填状态:0-未回填,1-部分回填,2-已回填
|
||||||
|
|
||||||
// 回填状态文本
|
// 回填状态文本
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.lukouguoji.module_base.bean
|
||||||
|
|
||||||
|
import androidx.databinding.ObservableBoolean
|
||||||
|
import com.lukouguoji.module_base.interfaces.ICheck
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际进港装机单 ULD 主单 Bean
|
||||||
|
* 对应接口:/IntImpManifest/pageQueryAir 返回的列表项(按 ULD 分组,manifestList 为关联运单)
|
||||||
|
*/
|
||||||
|
data class GjjLoadingUld(
|
||||||
|
var uld: String? = null, // ULD 编号
|
||||||
|
var flight: String? = null, // 航班信息
|
||||||
|
var totalPc: Long? = null, // 总件数
|
||||||
|
var totalWeight: Double? = null, // 总重量
|
||||||
|
var manifestList: List<GjjManifest>? = null, // 关联运单列表
|
||||||
|
|
||||||
|
// 展开/收起状态
|
||||||
|
@Transient
|
||||||
|
val showMore: ObservableBoolean = ObservableBoolean(false),
|
||||||
|
|
||||||
|
// 选中状态
|
||||||
|
@Transient
|
||||||
|
val checked: ObservableBoolean = ObservableBoolean(false)
|
||||||
|
) : Serializable, ICheck {
|
||||||
|
|
||||||
|
override fun getCheckObservable(): ObservableBoolean = checked
|
||||||
|
|
||||||
|
var isSelected: Boolean
|
||||||
|
get() = checked.get()
|
||||||
|
set(value) = checked.set(value)
|
||||||
|
|
||||||
|
/** 总重量显示(取整) */
|
||||||
|
val totalWeightText: String
|
||||||
|
get() = ((totalWeight ?: 0.0).toInt()).toString()
|
||||||
|
}
|
||||||
@@ -12,6 +12,8 @@ data class GjjManifest(
|
|||||||
var no: String = "", // 编号
|
var no: String = "", // 编号
|
||||||
var prefix: String = "", // 前缀
|
var prefix: String = "", // 前缀
|
||||||
var wbNo: String = "", // 运单号
|
var wbNo: String = "", // 运单号
|
||||||
|
var fdate: String = "", // 航班日期
|
||||||
|
var fno: String = "", // 航班号
|
||||||
var origin: String = "", // 货源地(始发站)
|
var origin: String = "", // 货源地(始发站)
|
||||||
var dest: String = "", // 目的站
|
var dest: String = "", // 目的站
|
||||||
var fdep: String = "", // 航班起始站
|
var fdep: String = "", // 航班起始站
|
||||||
@@ -58,6 +60,7 @@ data class GjjManifest(
|
|||||||
var unNumber: String = "", // 危险品编号
|
var unNumber: String = "", // 危险品编号
|
||||||
var activeId: Long = 0, // 活动ID
|
var activeId: Long = 0, // 活动ID
|
||||||
var locationTally: String = "", // 理货库位号
|
var locationTally: String = "", // 理货库位号
|
||||||
|
var locationTallyId: Long = 0, // 理货库位ID
|
||||||
var pic: String = "", // 交接图片缩略图路径
|
var pic: String = "", // 交接图片缩略图路径
|
||||||
var originalPic: String = "", // 交接图片原图地址
|
var originalPic: String = "", // 交接图片原图地址
|
||||||
var picNumber: String = "" // 交接图片数量
|
var picNumber: String = "" // 交接图片数量
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ data class ManifestTotalDto(
|
|||||||
var cargoWeight: Double = 0.0, // 总货重
|
var cargoWeight: Double = 0.0, // 总货重
|
||||||
|
|
||||||
// ===== 后端统一统计 DTO 的分项计数(2026-09 后端补齐,按接口各取所需) =====
|
// ===== 后端统一统计 DTO 的分项计数(2026-09 后端补齐,按接口各取所需) =====
|
||||||
var flightNumber: Int = 0, // 航班数
|
var flightNumber: Int = 0, // 航班航次
|
||||||
var uldNumber: Int = 0, // 板箱数(出库交接 Tab 角标)
|
var uldNumber: Int = 0, // 板箱数(出库交接 Tab 角标)
|
||||||
var storedNumber: Int = 0, // 已入库票数(出港/进港查询)
|
var storedNumber: Int = 0, // 已入库票数(出港/进港查询)
|
||||||
var departedNumber: Int = 0, // 已离港(出港)/ 已出库(进港)票数
|
var departedNumber: Int = 0, // 已离港(出港)/ 已出库(进港)票数
|
||||||
|
|||||||
@@ -13,4 +13,7 @@ class UldInfoBean {
|
|||||||
var totalWeight: String = "" // 总重量
|
var totalWeight: String = "" // 总重量
|
||||||
var status: String = "" // 状态(旧字段,保留兼容)
|
var status: String = "" // 状态(旧字段,保留兼容)
|
||||||
var useId: Long? = null // ULD使用ID(来自getUld接口)
|
var useId: Long? = null // ULD使用ID(来自getUld接口)
|
||||||
|
var dgrCode: String = "" // IMP代码
|
||||||
|
var boardType: String = "" // 板型(可下拉选择,也可手动输入)
|
||||||
|
var bupFlag: String = "0" // BUP选项(0:否;1:是),默认否
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,6 +348,9 @@ interface Constant {
|
|||||||
|
|
||||||
// 数据
|
// 数据
|
||||||
const val DATA = "data"
|
const val DATA = "data"
|
||||||
|
|
||||||
|
// 页面标题
|
||||||
|
const val TITLE = "title"
|
||||||
const val POSITION = "position"
|
const val POSITION = "position"
|
||||||
|
|
||||||
// 单号
|
// 单号
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import com.lukouguoji.module_base.bean.GjcHandoverSheetResponse
|
|||||||
import com.lukouguoji.module_base.bean.GjcInspectionBean
|
import com.lukouguoji.module_base.bean.GjcInspectionBean
|
||||||
import com.lukouguoji.module_base.bean.GjcMaWb
|
import com.lukouguoji.module_base.bean.GjcMaWb
|
||||||
import com.lukouguoji.module_base.bean.GjcMove
|
import com.lukouguoji.module_base.bean.GjcMove
|
||||||
|
import com.lukouguoji.module_base.bean.GjcMoveDetail
|
||||||
import com.lukouguoji.module_base.bean.GjcUldUseBean
|
import com.lukouguoji.module_base.bean.GjcUldUseBean
|
||||||
import com.lukouguoji.module_base.bean.GjcWarehouse
|
import com.lukouguoji.module_base.bean.GjcWarehouse
|
||||||
import com.lukouguoji.module_base.bean.GjcWaybillBean
|
import com.lukouguoji.module_base.bean.GjcWaybillBean
|
||||||
@@ -56,9 +57,11 @@ import com.lukouguoji.module_base.bean.IntImpAccidentVisaBean
|
|||||||
import com.lukouguoji.module_base.bean.IntImpPickUpDLVBean
|
import com.lukouguoji.module_base.bean.IntImpPickUpDLVBean
|
||||||
import com.lukouguoji.module_base.bean.IntImpPickUpRecordBean
|
import com.lukouguoji.module_base.bean.IntImpPickUpRecordBean
|
||||||
import com.lukouguoji.module_base.bean.IntImpQueryBean
|
import com.lukouguoji.module_base.bean.IntImpQueryBean
|
||||||
|
import com.lukouguoji.module_base.bean.GjjLoadingUld
|
||||||
import com.lukouguoji.module_base.bean.GjjManifest
|
import com.lukouguoji.module_base.bean.GjjManifest
|
||||||
import com.lukouguoji.module_base.bean.GjjManifestBean
|
import com.lukouguoji.module_base.bean.GjjManifestBean
|
||||||
import com.lukouguoji.module_base.bean.GjjPackTypeBean
|
import com.lukouguoji.module_base.bean.GjjPackTypeBean
|
||||||
|
import com.lukouguoji.module_base.bean.GjjStorageUse
|
||||||
import com.lukouguoji.module_base.bean.GjjTallyBean
|
import com.lukouguoji.module_base.bean.GjjTallyBean
|
||||||
import com.lukouguoji.module_base.bean.GjjTallyDetailsBean
|
import com.lukouguoji.module_base.bean.GjjTallyDetailsBean
|
||||||
import com.lukouguoji.module_base.bean.GjjTallyRecordBean
|
import com.lukouguoji.module_base.bean.GjjTallyRecordBean
|
||||||
@@ -147,6 +150,14 @@ interface Api {
|
|||||||
@POST
|
@POST
|
||||||
suspend fun singleStringPost(@Url url: String): BaseResultBean<String>
|
suspend fun singleStringPost(@Url url: String): BaseResultBean<String>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取海关回执(国际进港原始舱单/进港理货/出港运抵/出港装载/出港理货 共用)
|
||||||
|
* url:XXX/getResponse,body 传 prefix、no 与拼接的 wbNo(后端实测按 prefix + no 匹配),
|
||||||
|
* 回执文本实测在 msg 字段、data 为 null,取值见 CustomsResponseKtx.showCustomsResponse
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
suspend fun getCustomsResponse(@Url url: String, @Body data: RequestBody): BaseResultBean<String>
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
suspend fun anyGetForId(@Url url: String, @Query("id") id: String): BaseResultBean<Any>
|
suspend fun anyGetForId(@Url url: String, @Query("id") id: String): BaseResultBean<Any>
|
||||||
|
|
||||||
@@ -302,6 +313,24 @@ interface Api {
|
|||||||
@POST("typeCode/countryType")
|
@POST("typeCode/countryType")
|
||||||
suspend fun getCountryTypeList(): DictListBean
|
suspend fun getCountryTypeList(): DictListBean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取飞行状态
|
||||||
|
*/
|
||||||
|
@POST("typeCode/flightPortCode")
|
||||||
|
suspend fun getFlightPortCodeList(): DictListBean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取航班服务种类
|
||||||
|
*/
|
||||||
|
@POST("typeCode/flightServiceType")
|
||||||
|
suspend fun getFlightServiceTypeList(): DictListBean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取航班状态
|
||||||
|
*/
|
||||||
|
@POST("typeCode/flightStatus")
|
||||||
|
suspend fun getFlightStatusList(): DictListBean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取国家代码
|
* 获取国家代码
|
||||||
*/
|
*/
|
||||||
@@ -564,6 +593,14 @@ interface Api {
|
|||||||
@POST("typeCode/assembleCompany")
|
@POST("typeCode/assembleCompany")
|
||||||
suspend fun getAssembleCompanyList(): BaseResultBean<List<AssembleCompanyBean>>
|
suspend fun getAssembleCompanyList(): BaseResultBean<List<AssembleCompanyBean>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取板型下拉列表(国际出港组装-开始组装)
|
||||||
|
* 接口路径: /typeCode/bordType(后端路径即为 bordType,非 boardType,2026-09-11 实测 boardType 返回 404)
|
||||||
|
* @param carrier 承运人二字码(取待组装运单航班号前两位);该参数后端必填(缺省返回 400),进入页面时传空串
|
||||||
|
*/
|
||||||
|
@POST("typeCode/bordType")
|
||||||
|
suspend fun getBoardTypeList(@Query("carrier") carrier: String): DictListBean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港板箱过磅-分页搜索
|
* 国际出港板箱过磅-分页搜索
|
||||||
* 接口路径: /IntExpWeighting/pageQuery
|
* 接口路径: /IntExpWeighting/pageQuery
|
||||||
@@ -657,7 +694,8 @@ interface Api {
|
|||||||
* 国际出港组装 - 根据全ULD查询ULD状态和耗材重量
|
* 国际出港组装 - 根据全ULD查询ULD状态和耗材重量
|
||||||
* 接口路径: /IntExpAssemble/getUld
|
* 接口路径: /IntExpAssemble/getUld
|
||||||
* @param uld ULD编号
|
* @param uld ULD编号
|
||||||
* @return ULD信息,包含status(0:正常,1:故障)和consumeWeight(耗材重量)
|
* @return ULD信息,包含status(0:正常,1:故障)、consumeWeight(耗材重量)、
|
||||||
|
* dgrCode(IMP代码)、boardType(板型)、bupFlag(BUP选项 0:否;1:是)
|
||||||
*/
|
*/
|
||||||
@GET("IntExpAssemble/getUld")
|
@GET("IntExpAssemble/getUld")
|
||||||
suspend fun getUldWithConsumeWeight(@Query("uld") uld: String): BaseResultBean<GjcUldUseBean>
|
suspend fun getUldWithConsumeWeight(@Query("uld") uld: String): BaseResultBean<GjcUldUseBean>
|
||||||
@@ -666,7 +704,8 @@ interface Api {
|
|||||||
* 国际出港组装 - 卸货
|
* 国际出港组装 - 卸货
|
||||||
* 接口路径: /IntExpAssemble/drop
|
* 接口路径: /IntExpAssemble/drop
|
||||||
* @param data 请求参数:abPc(组装件数)、abWeight(组装重量)、consumeWeight(耗材重量)、
|
* @param data 请求参数:abPc(组装件数)、abWeight(组装重量)、consumeWeight(耗材重量)、
|
||||||
* ldId(组装人)、loadArea(组装区)、useInfo(ULD信息)、wbInfo(运单信息)、userId(用户ID)
|
* ldId(组装人)、loadArea(组装区)、useInfo(ULD信息)、wbInfo(运单信息)、userId(用户ID)、
|
||||||
|
* dgrCode(IMP代码)、boardType(板型)、bupFlag(BUP选项 0:否;1:是)
|
||||||
*/
|
*/
|
||||||
@POST("IntExpAssemble/drop")
|
@POST("IntExpAssemble/drop")
|
||||||
suspend fun assembleDropCargo(@Body data: RequestBody): BaseResultBean<GjcWarehouse>
|
suspend fun assembleDropCargo(@Body data: RequestBody): BaseResultBean<GjcWarehouse>
|
||||||
@@ -675,7 +714,8 @@ interface Api {
|
|||||||
* 国际出港组装 - 装货
|
* 国际出港组装 - 装货
|
||||||
* 接口路径: /IntExpAssemble/assemble
|
* 接口路径: /IntExpAssemble/assemble
|
||||||
* @param data 请求参数:abPc(组装件数)、abWeight(组装重量)、consumeWeight(耗材重量)、
|
* @param data 请求参数:abPc(组装件数)、abWeight(组装重量)、consumeWeight(耗材重量)、
|
||||||
* ldId(组装人)、loadArea(组装区)、useInfo(ULD信息)、wbInfo(运单信息)、userId(用户ID)
|
* ldId(组装人)、loadArea(组装区)、useInfo(ULD信息)、wbInfo(运单信息)、userId(用户ID)、
|
||||||
|
* dgrCode(IMP代码)、boardType(板型)、bupFlag(BUP选项 0:否;1:是)
|
||||||
*/
|
*/
|
||||||
@POST("IntExpAssemble/assemble")
|
@POST("IntExpAssemble/assemble")
|
||||||
suspend fun assembleLoadCargo(@Body data: RequestBody): BaseResultBean<GjcWarehouse>
|
suspend fun assembleLoadCargo(@Body data: RequestBody): BaseResultBean<GjcWarehouse>
|
||||||
@@ -737,6 +777,14 @@ interface Api {
|
|||||||
@POST("IntExpOutHandover/waitingTrans")
|
@POST("IntExpOutHandover/waitingTrans")
|
||||||
suspend fun waitingTrans(@Body data: RequestBody): BaseResultBean<String>
|
suspend fun waitingTrans(@Body data: RequestBody): BaseResultBean<String>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际出港出库交接-允许装机
|
||||||
|
* 接口路径: /IntExpOutHandover/allow
|
||||||
|
* @param data 请求参数:选中的ULD列表(GjcUldUseBean 数组)
|
||||||
|
*/
|
||||||
|
@POST("IntExpOutHandover/allow")
|
||||||
|
suspend fun allowLoading(@Body data: RequestBody): BaseResultBean<Boolean>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港-出港装载 分页查询
|
* 国际出港-出港装载 分页查询
|
||||||
* 接口路径: /IntExpLoad/pageQuery
|
* 接口路径: /IntExpLoad/pageQuery
|
||||||
@@ -882,6 +930,20 @@ interface Api {
|
|||||||
@POST("IntExpMove/move")
|
@POST("IntExpMove/move")
|
||||||
suspend fun submitIntExpMove(@Body data: RequestBody): BaseResultBean<SimpleResultBean>
|
suspend fun submitIntExpMove(@Body data: RequestBody): BaseResultBean<SimpleResultBean>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际出港移库-详情
|
||||||
|
* 接口路径: /IntExpMove/detail
|
||||||
|
*/
|
||||||
|
@POST("IntExpMove/detail")
|
||||||
|
suspend fun getIntExpMoveDetail(@Query("maWbId") maWbId: Long): BaseResultBean<GjcMoveDetail>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际出港移库-编辑(仅更新备注与交接图片)
|
||||||
|
* 接口路径: /IntExpMove/update
|
||||||
|
*/
|
||||||
|
@POST("IntExpMove/update")
|
||||||
|
suspend fun updateIntExpMove(@Body data: RequestBody): BaseResultBean<String>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港库位操作-清仓
|
* 国际出港库位操作-清仓
|
||||||
* 接口路径: /IntExpStorageUse/updateClear
|
* 接口路径: /IntExpStorageUse/updateClear
|
||||||
@@ -958,7 +1020,7 @@ interface Api {
|
|||||||
* 接口路径: /IntImpStorage/updateClear
|
* 接口路径: /IntImpStorage/updateClear
|
||||||
*/
|
*/
|
||||||
@POST("IntImpStorage/updateClear")
|
@POST("IntImpStorage/updateClear")
|
||||||
suspend fun clearIntImpStorage(@Query("clearNormal") clearNormal: String, @Body data: RequestBody): BaseResultBean<Boolean>
|
suspend fun clearIntImpStorage(@Body data: RequestBody): BaseResultBean<Boolean>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际进港库位操作-修改库位
|
* 国际进港库位操作-修改库位
|
||||||
@@ -1401,6 +1463,24 @@ interface Api {
|
|||||||
@POST("flt/listFlightDest")
|
@POST("flt/listFlightDest")
|
||||||
suspend fun listFlightDest(@Body data: RequestBody): BaseResultBean<List<String>>
|
suspend fun listFlightDest(@Body data: RequestBody): BaseResultBean<List<String>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 航班管理-新增航班
|
||||||
|
*/
|
||||||
|
@POST("flt/saveFlight")
|
||||||
|
suspend fun saveFlight(@Body data: RequestBody): BaseResultBean<String>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 航班管理-修改航班
|
||||||
|
*/
|
||||||
|
@POST("flt/updateFlight")
|
||||||
|
suspend fun updateFlight(@Body data: RequestBody): BaseResultBean<String>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 航班管理-删除航班(请求体为航班对象数组)
|
||||||
|
*/
|
||||||
|
@POST("flt/delete")
|
||||||
|
suspend fun deleteFlightList(@Body data: RequestBody): BaseResultBean<String>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增或修航司屏蔽
|
* 新增或修航司屏蔽
|
||||||
*/
|
*/
|
||||||
@@ -1914,6 +1994,14 @@ interface Api {
|
|||||||
@POST("IntImpManifest/pageQueryTotal")
|
@POST("IntImpManifest/pageQueryTotal")
|
||||||
suspend fun getIntImpManifestTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
|
suspend fun getIntImpManifestTotal(@Body data: RequestBody): BaseResultBean<ManifestTotalDto>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际进港舱单-根据11位具体运单号查询运单信息
|
||||||
|
* 接口路径: /IntImpManifest/queryWbByNo
|
||||||
|
* @param wbNo 11位运单号
|
||||||
|
*/
|
||||||
|
@POST("IntImpManifest/queryWbByNo")
|
||||||
|
suspend fun getIntImpManifestByWbNo(@Query("wbNo") wbNo: String): BaseResultBean<GjjManifest>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际进港舱单-获取主单下分单
|
* 国际进港舱单-获取主单下分单
|
||||||
*/
|
*/
|
||||||
@@ -1939,10 +2027,10 @@ interface Api {
|
|||||||
suspend fun getMaWbMinusHaWb(@Body data: RequestBody): BaseResultBean<GjjHaWb>
|
suspend fun getMaWbMinusHaWb(@Body data: RequestBody): BaseResultBean<GjjHaWb>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际进港舱单-分拣理货(装机单)-分页查询
|
* 国际进港舱单-分拣理货(装机单)-分页查询(按 ULD 分组,manifestList 为关联运单)
|
||||||
*/
|
*/
|
||||||
@POST("IntImpManifest/pageQueryAir")
|
@POST("IntImpManifest/pageQueryAir")
|
||||||
suspend fun getIntImpLoadingList(@Body data: RequestBody): PageInfo<GjjManifest>
|
suspend fun getIntImpLoadingList(@Body data: RequestBody): PageInfo<GjjLoadingUld>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际进港舱单-分拣理货(装机单)-分页合计
|
* 国际进港舱单-分拣理货(装机单)-分页合计
|
||||||
@@ -1970,6 +2058,16 @@ interface Api {
|
|||||||
@POST("IntImpManifest/inStorage")
|
@POST("IntImpManifest/inStorage")
|
||||||
suspend fun inIntImpLoadingStorage(@Body data: RequestBody): BaseResultBean<Boolean>
|
suspend fun inIntImpLoadingStorage(@Body data: RequestBody): BaseResultBean<Boolean>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际进港舱单-获取运单理货库位
|
||||||
|
* 接口路径: /IntImpManifest/getSortingStorage
|
||||||
|
*/
|
||||||
|
@POST("IntImpManifest/getSortingStorage")
|
||||||
|
suspend fun getIntImpSortingStorage(
|
||||||
|
@Query("fid") fid: Long,
|
||||||
|
@Query("wbNo") wbNo: String
|
||||||
|
): BaseResultBean<GjjStorageUse>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际进港理货报告-分页查询
|
* 国际进港理货报告-分页查询
|
||||||
*/
|
*/
|
||||||
@@ -2070,6 +2168,12 @@ interface Api {
|
|||||||
@POST("typeCode/unusualType")
|
@POST("typeCode/unusualType")
|
||||||
suspend fun getUnusualTypeList(): DictListBean
|
suspend fun getUnusualTypeList(): DictListBean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典-发现时间类型(接收时 / 分拣理货时 / 提货时 …)
|
||||||
|
*/
|
||||||
|
@POST("typeCode/searchDateType")
|
||||||
|
suspend fun getSearchDateTypeList(): DictListBean
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// ULD管理
|
// ULD管理
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.lukouguoji.module_base.ktx
|
||||||
|
|
||||||
|
import com.lukouguoji.module_base.base.BaseViewModel
|
||||||
|
import com.lukouguoji.module_base.http.net.NetApply
|
||||||
|
import com.lukouguoji.module_base.ui.page.customs.CustomsResponseActivity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 海关回执接口地址(各页面对应各自模块的 getResponse)
|
||||||
|
*/
|
||||||
|
object CustomsResponseUrl {
|
||||||
|
const val INT_IMP_AIR_MANIFEST = "IntImpAirManifest/getResponse" // 国际进港原始舱单
|
||||||
|
const val INT_IMP_TALLY = "IntImpTally/getResponse" // 国际进港理货
|
||||||
|
const val INT_EXP_ARRIVE = "IntExpArrive/getResponse" // 国际出港运抵
|
||||||
|
const val INT_EXP_LOAD = "IntExpLoad/getResponse" // 国际出港装载
|
||||||
|
const val INT_EXP_TALLY = "IntExpTally/getResponse" // 国际出港理货
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表项侧滑「回执」按钮的 onItemClick type
|
||||||
|
* 现有取值为 0/1/2、101~103、1000~3000 及 view id,9001 与之均不冲突
|
||||||
|
*/
|
||||||
|
const val ITEM_CLICK_TYPE_CUSTOMS_RESPONSE = 9001
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询海关回执并跳转只读详情页
|
||||||
|
* - 带 Loading 调用 [url],本期只查主单,不传 hno
|
||||||
|
* - 请求体同时传 prefix、no 与拼接后的 wbNo:测试环境实测后端按 prefix + no 匹配,
|
||||||
|
* 只传 wbNo 会查不到(与文档「运单号必填」描述不一致)
|
||||||
|
* - 回执文本实测放在 msg 字段(data 恒为 null),这里优先取 data、为空再取 msg,兼容两种返回
|
||||||
|
* - 回执为空时提示「暂无海关回执」,不跳转
|
||||||
|
* - 接口失败由 launchLoadingCollect 默认 Toast 错误信息
|
||||||
|
*/
|
||||||
|
fun BaseViewModel.showCustomsResponse(url: String, prefix: String?, no: String?, title: String) {
|
||||||
|
if (prefix.isNullOrEmpty() || no.isNullOrEmpty()) {
|
||||||
|
showToast("运单号为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val params = mapOf(
|
||||||
|
"wbNo" to prefix + no,
|
||||||
|
"prefix" to prefix,
|
||||||
|
"no" to no
|
||||||
|
).toRequestBody()
|
||||||
|
launchLoadingCollect({ NetApply.api.getCustomsResponse(url, params) }) {
|
||||||
|
onSuccess = { result ->
|
||||||
|
val content = result.data?.ifEmpty { null } ?: result.msg ?: ""
|
||||||
|
if (content.isEmpty()) {
|
||||||
|
showToast("暂无海关回执")
|
||||||
|
} else {
|
||||||
|
CustomsResponseActivity.start(getTopActivity(), title, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,3 +14,9 @@ fun Number.limit(count: Int = 2): String {
|
|||||||
}
|
}
|
||||||
return DecimalFormat(pattern.toString()).format(this)
|
return DecimalFormat(pattern.toString()).format(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去掉无意义的小数位:12.0 -> "12",12.50 -> "12.5"
|
||||||
|
*/
|
||||||
|
fun Double.toPlainText(): String =
|
||||||
|
if (this % 1.0 == 0.0) toLong().toString() else toBigDecimal().stripTrailingZeros().toPlainString()
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ object ARouterConstants {
|
|||||||
const val ACTIVITY_URL_INT_EXP_ASSEMBLE = "/gjc/IntExpAssembleActivity" //国际出港 出港组装
|
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_ASSEMBLE_START = "/gjc/IntExpAssembleStartActivity" //国际出港 开始组装
|
||||||
const val ACTIVITY_URL_INT_EXP_MOVE = "/gjc/IntExpMoveActivity" //国际出港 出港移库
|
const val ACTIVITY_URL_INT_EXP_MOVE = "/gjc/IntExpMoveActivity" //国际出港 出港移库
|
||||||
|
const val ACTIVITY_URL_INT_EXP_MOVE_EDIT = "/gjc/IntExpMoveEditActivity" //国际出港 出港移库编辑/详情
|
||||||
const val ACTIVITY_URL_GJC_ASSEMBLE_ALLOCATE = "/gjc/GjcAssembleAllocateActivity" //国际出港 组装分配
|
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_OUT_HANDOVER = "/gjc/IntExpOutHandoverActivity" //国际出港 出库交接
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.lukouguoji.module_base.ui.page.customs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import com.lukouguoji.module_base.R
|
||||||
|
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||||
|
import com.lukouguoji.module_base.common.Constant
|
||||||
|
import com.lukouguoji.module_base.databinding.ActivityCustomsResponseBinding
|
||||||
|
import com.lukouguoji.module_base.impl.EmptyViewModel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 海关回执详情页(只读)
|
||||||
|
* 回执文本由 Intent 传入,页面内不调接口;UI 参照国际进港电报详情页
|
||||||
|
*/
|
||||||
|
class CustomsResponseActivity :
|
||||||
|
BaseBindingActivity<ActivityCustomsResponseBinding, EmptyViewModel>() {
|
||||||
|
|
||||||
|
override fun layoutId() = R.layout.activity_customs_response
|
||||||
|
|
||||||
|
override fun viewModelClass() = EmptyViewModel::class.java
|
||||||
|
|
||||||
|
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||||
|
setBackArrow(intent.getStringExtra(Constant.Key.TITLE) ?: "海关回执")
|
||||||
|
|
||||||
|
binding.etContent.apply {
|
||||||
|
setText(intent.getStringExtra(Constant.Key.DATA) ?: "")
|
||||||
|
// 只读:不可输入、无光标,但仍可长按选择复制
|
||||||
|
keyListener = null
|
||||||
|
isFocusable = false
|
||||||
|
isFocusableInTouchMode = false
|
||||||
|
isCursorVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun start(context: Context, title: String, content: String) {
|
||||||
|
context.startActivity(
|
||||||
|
Intent(context, CustomsResponseActivity::class.java)
|
||||||
|
.putExtra(Constant.Key.TITLE, title)
|
||||||
|
.putExtra(Constant.Key.DATA, content)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -211,7 +211,7 @@ class PadDataLayout : FrameLayout {
|
|||||||
setOnClickListener(null)
|
setOnClickListener(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
DataLayoutType.DATE -> {
|
DataLayoutType.DATE, DataLayoutType.DATETIME -> {
|
||||||
tv.visibility = VISIBLE
|
tv.visibility = VISIBLE
|
||||||
et.visibility = GONE
|
et.visibility = GONE
|
||||||
spinner.visibility = GONE
|
spinner.visibility = GONE
|
||||||
@@ -258,4 +258,5 @@ enum class DataLayoutType {
|
|||||||
INPUT, // 文本
|
INPUT, // 文本
|
||||||
SPINNER, // 下拉选择
|
SPINNER, // 下拉选择
|
||||||
DATE, // 日期
|
DATE, // 日期
|
||||||
|
DATETIME, // 日期时间(yyyy-MM-dd HH:mm:ss)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.lukouguoji.module_base.adapter.visible
|
|||||||
import com.lukouguoji.module_base.interfaces.IOnFocusChangeListener
|
import com.lukouguoji.module_base.interfaces.IOnFocusChangeListener
|
||||||
import com.lukouguoji.module_base.interfaces.IOnSpinnerSelected
|
import com.lukouguoji.module_base.interfaces.IOnSpinnerSelected
|
||||||
import com.lukouguoji.module_base.ktx.formatDate
|
import com.lukouguoji.module_base.ktx.formatDate
|
||||||
|
import com.lukouguoji.module_base.ktx.formatDateTime
|
||||||
import com.lukouguoji.module_base.ktx.getActivity
|
import com.lukouguoji.module_base.ktx.getActivity
|
||||||
import com.lukouguoji.module_base.ktx.loge
|
import com.lukouguoji.module_base.ktx.loge
|
||||||
import com.lukouguoji.module_base.util.Common
|
import com.lukouguoji.module_base.util.Common
|
||||||
@@ -182,6 +183,18 @@ class PadDataLayoutNew : FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 选择日期时间(yyyy-MM-dd HH:mm:ss)
|
||||||
|
private val dateTimeClick: (v: View) -> Unit = {
|
||||||
|
if (enable) {
|
||||||
|
Common.onYearMonthDayTime(context.getActivity(), value) { year, month, day, hour, minute, second ->
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
calendar.set(year, month - 1, day, hour, minute, second)
|
||||||
|
value = calendar.time.formatDateTime()
|
||||||
|
refreshCallBack?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initView()
|
initView()
|
||||||
}
|
}
|
||||||
@@ -252,7 +265,7 @@ class PadDataLayoutNew : FrameLayout {
|
|||||||
setOnClickListener(null)
|
setOnClickListener(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
DataLayoutType.DATE -> {
|
DataLayoutType.DATE, DataLayoutType.DATETIME -> {
|
||||||
tv.visibility = VISIBLE
|
tv.visibility = VISIBLE
|
||||||
et.visibility = GONE
|
et.visibility = GONE
|
||||||
spinner.visibility = GONE
|
spinner.visibility = GONE
|
||||||
@@ -263,7 +276,7 @@ class PadDataLayoutNew : FrameLayout {
|
|||||||
iv.layoutParams.width = dp20
|
iv.layoutParams.width = dp20
|
||||||
iv.layoutParams.height = dp20
|
iv.layoutParams.height = dp20
|
||||||
|
|
||||||
setOnClickListener(dateClick)
|
setOnClickListener(if (type == DataLayoutType.DATETIME) dateTimeClick else dateClick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import com.lukouguoji.module_base.R
|
|||||||
/**
|
/**
|
||||||
* 手机版底部操作条(5.5 寸手持端公共组件)
|
* 手机版底部操作条(5.5 寸手持端公共组件)
|
||||||
*
|
*
|
||||||
* 结构:全选(圆形勾选框 + 文字)+ 已选统计 + 主操作按钮。
|
* 结构:全选(圆形勾选框 + 文字)+ 已选统计 + 次操作按钮(可选,最多 2 个)+ 主操作按钮。
|
||||||
|
* 平板底部有多个批量操作按钮的页面,用 subAction1Text/subAction2Text 承载其余操作(如出库交接「允许装机」「配运」)。
|
||||||
* 对应设计稿各多选列表页底部的白色操作条。
|
* 对应设计稿各多选列表页底部的白色操作条。
|
||||||
*
|
*
|
||||||
* XML 用法:
|
* XML 用法:
|
||||||
@@ -39,6 +40,8 @@ class PhoneBottomBar @JvmOverloads constructor(
|
|||||||
val ivCheckAll: ImageView
|
val ivCheckAll: ImageView
|
||||||
val tvCount: TextView
|
val tvCount: TextView
|
||||||
val tvAction: TextView
|
val tvAction: TextView
|
||||||
|
val tvSubAction1: TextView
|
||||||
|
val tvSubAction2: TextView
|
||||||
|
|
||||||
/** 全选状态(驱动勾选框图标切换) */
|
/** 全选状态(驱动勾选框图标切换) */
|
||||||
var allChecked: Boolean = false
|
var allChecked: Boolean = false
|
||||||
@@ -64,6 +67,22 @@ class PhoneBottomBar @JvmOverloads constructor(
|
|||||||
tvAction.text = v
|
tvAction.text = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 次操作按钮 1 文字(空则隐藏) */
|
||||||
|
var subAction1Text: String = ""
|
||||||
|
set(v) {
|
||||||
|
field = v
|
||||||
|
tvSubAction1.text = v
|
||||||
|
tvSubAction1.visibility = if (v.isEmpty()) GONE else VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 次操作按钮 2 文字(空则隐藏) */
|
||||||
|
var subAction2Text: String = ""
|
||||||
|
set(v) {
|
||||||
|
field = v
|
||||||
|
tvSubAction2.text = v
|
||||||
|
tvSubAction2.visibility = if (v.isEmpty()) GONE else VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 危险操作样式:浅红底 + 红字 + 垃圾桶图标(如 ULD 管理的批量删除)。
|
* 危险操作样式:浅红底 + 红字 + 垃圾桶图标(如 ULD 管理的批量删除)。
|
||||||
* 默认 false 为主色实底按钮,既有页面不受影响。
|
* 默认 false 为主色实底按钮,既有页面不受影响。
|
||||||
@@ -102,6 +121,8 @@ class PhoneBottomBar @JvmOverloads constructor(
|
|||||||
ivCheckAll = findViewById(R.id.iv_check_all)
|
ivCheckAll = findViewById(R.id.iv_check_all)
|
||||||
tvCount = findViewById(R.id.tv_count)
|
tvCount = findViewById(R.id.tv_count)
|
||||||
tvAction = findViewById(R.id.tv_action)
|
tvAction = findViewById(R.id.tv_action)
|
||||||
|
tvSubAction1 = findViewById(R.id.tv_sub_action1)
|
||||||
|
tvSubAction2 = findViewById(R.id.tv_sub_action2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 只保存/恢复自身状态,避免内部子控件 id 与其他复合组件实例串档 */
|
/** 只保存/恢复自身状态,避免内部子控件 id 与其他复合组件实例串档 */
|
||||||
|
|||||||
@@ -288,3 +288,23 @@ fun setPhoneFormRowRefreshCallBack(row: PhoneFormRow, callBack: (() -> Unit)?) {
|
|||||||
fun setPhoneBottomBarActionDanger(bar: PhoneBottomBar, danger: Boolean?) {
|
fun setPhoneBottomBarActionDanger(bar: PhoneBottomBar, danger: Boolean?) {
|
||||||
danger?.let { bar.actionDanger = it }
|
danger?.let { bar.actionDanger = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// PhoneBottomBar(追加:次操作按钮,承载平板底部其余批量操作)
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@BindingAdapter("subAction1Text", "subAction2Text", requireAll = false)
|
||||||
|
fun setPhoneBottomBarSubActions(bar: PhoneBottomBar, subAction1Text: String?, subAction2Text: String?) {
|
||||||
|
subAction1Text?.let { bar.subAction1Text = it }
|
||||||
|
subAction2Text?.let { bar.subAction2Text = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindingAdapter("setOnSubAction1Click", requireAll = false)
|
||||||
|
fun setPhoneBottomBarSubAction1Click(bar: PhoneBottomBar, listener: View.OnClickListener?) {
|
||||||
|
bar.tvSubAction1.setOnClickListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindingAdapter("setOnSubAction2Click", requireAll = false)
|
||||||
|
fun setPhoneBottomBarSubAction2Click(bar: PhoneBottomBar, listener: View.OnClickListener?) {
|
||||||
|
bar.tvSubAction2.setOnClickListener(listener)
|
||||||
|
}
|
||||||
|
|||||||
@@ -490,6 +490,60 @@ object DictUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 飞行状态(0:前方起飞 1:过站登机 2:登机结束 6:到达 7:登机 9:正在值机 …)
|
||||||
|
*/
|
||||||
|
fun getFlightPortCodeList(
|
||||||
|
addAll: Boolean = true,
|
||||||
|
checkedValue: String? = null,
|
||||||
|
callBack: (List<KeyValue>) -> Unit
|
||||||
|
) {
|
||||||
|
launchCollect({
|
||||||
|
NetApply.api
|
||||||
|
.getFlightPortCodeList()
|
||||||
|
}) {
|
||||||
|
onSuccess = {
|
||||||
|
handleCallBack(it, checkedValue, addAll, callBack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 航班服务种类(0:客机 1:货机 2:卡车)
|
||||||
|
*/
|
||||||
|
fun getFlightServiceTypeList(
|
||||||
|
addAll: Boolean = true,
|
||||||
|
checkedValue: String? = null,
|
||||||
|
callBack: (List<KeyValue>) -> Unit
|
||||||
|
) {
|
||||||
|
launchCollect({
|
||||||
|
NetApply.api
|
||||||
|
.getFlightServiceTypeList()
|
||||||
|
}) {
|
||||||
|
onSuccess = {
|
||||||
|
handleCallBack(it, checkedValue, addAll, callBack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 航班状态(3:返航 4:地面滑回 5:备降 8:取消 12:延误 15:删除 16:未知)
|
||||||
|
*/
|
||||||
|
fun getFlightStatusList(
|
||||||
|
addAll: Boolean = true,
|
||||||
|
checkedValue: String? = null,
|
||||||
|
callBack: (List<KeyValue>) -> Unit
|
||||||
|
) {
|
||||||
|
launchCollect({
|
||||||
|
NetApply.api
|
||||||
|
.getFlightStatusList()
|
||||||
|
}) {
|
||||||
|
onSuccess = {
|
||||||
|
handleCallBack(it, checkedValue, addAll, callBack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色列表
|
* 获取角色列表
|
||||||
*/
|
*/
|
||||||
@@ -625,6 +679,22 @@ object DictUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 板型列表(国际出港组装-开始组装)
|
||||||
|
* 接口 /typeCode/bordType
|
||||||
|
* @param carrier 承运人二字码(运单航班号前两位);为空时传空串(后端该参数必填,不传返回 400)
|
||||||
|
*/
|
||||||
|
fun getBoardTypeList(carrier: String? = null, callBack: (List<KeyValue>) -> Unit) {
|
||||||
|
launchCollect({
|
||||||
|
NetApply.api
|
||||||
|
.getBoardTypeList(carrier?.trim() ?: "")
|
||||||
|
}) {
|
||||||
|
onSuccess = {
|
||||||
|
callBack(it.data?.map { b -> b.toKeyValue() } ?: emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 报文类型
|
// 报文类型
|
||||||
fun getTelegramTypeList(callBack: (List<KeyValue>) -> Unit) {
|
fun getTelegramTypeList(callBack: (List<KeyValue>) -> Unit) {
|
||||||
launchCollect({
|
launchCollect({
|
||||||
|
|||||||
@@ -27,7 +27,12 @@ import com.lukouguoji.module_base.ktx.permission
|
|||||||
import com.lukouguoji.module_base.ktx.showToast
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
import com.lukouguoji.module_base.model.LoadingModel
|
import com.lukouguoji.module_base.model.LoadingModel
|
||||||
import dev.DevUtils
|
import dev.DevUtils
|
||||||
|
import dev.utils.app.HandlerUtils
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import okio.internal.commonToUtf8String
|
import okio.internal.commonToUtf8String
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
object PrinterUtils {
|
object PrinterUtils {
|
||||||
@@ -315,6 +320,50 @@ object PrinterUtils {
|
|||||||
* 打印国际出港板箱过磅挂签(2列布局,支持合并单元格)
|
* 打印国际出港板箱过磅挂签(2列布局,支持合并单元格)
|
||||||
*/
|
*/
|
||||||
fun printGjcBoxWeighing(bean: GjcUldUseBean) {
|
fun printGjcBoxWeighing(bean: GjcUldUseBean) {
|
||||||
|
printMergedGrid(buildGjcBoxWeighingRows(bean))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量打印国际出港板箱过磅挂签
|
||||||
|
* 逐张串行发送,张间节流,任一张发送失败即中止并提示已完成张数
|
||||||
|
*/
|
||||||
|
suspend fun printGjcBoxWeighingBatch(beans: List<GjcUldUseBean>) = withContext(Dispatchers.IO) {
|
||||||
|
var printed = 0
|
||||||
|
loading.showLoading()
|
||||||
|
try {
|
||||||
|
for ((index, bean) in beans.withIndex()) {
|
||||||
|
// 连接断开(含上一张写入异常后 SDK 置为断开)则不再继续发送
|
||||||
|
if (portManager?.connectStatus != true) break
|
||||||
|
HandlerUtils.postRunnable {
|
||||||
|
loading.setMessage("正在打印 ${index + 1}/${beans.size}")
|
||||||
|
}
|
||||||
|
val bytes = buildMergedGridBytes(buildGjcBoxWeighingRows(bean))
|
||||||
|
val ok = try {
|
||||||
|
portManager?.writeDataImmediately(bytes) == true
|
||||||
|
} catch (e: IOException) {
|
||||||
|
showLog("批量打印第 ${index + 1} 张写入异常 : ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
if (!ok) break
|
||||||
|
printed++
|
||||||
|
// 张间留出出纸时间,避免连续灌入导致打印机缓冲溢出
|
||||||
|
if (index < beans.lastIndex) delay(800)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.dismissLoading()
|
||||||
|
}
|
||||||
|
if (printed == beans.size) {
|
||||||
|
showToast("${beans.size} 张挂签打印完成")
|
||||||
|
} else {
|
||||||
|
val failBean = beans[printed]
|
||||||
|
showToast("已打印 $printed 张,第 ${printed + 1} 张(ULD ${failBean.uld})发送失败,请检查打印机后重试")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组装板箱过磅挂签的表格行数据
|
||||||
|
*/
|
||||||
|
private fun buildGjcBoxWeighingRows(bean: GjcUldUseBean): ArrayList<GridRow> {
|
||||||
val rows = arrayListOf<GridRow>()
|
val rows = arrayListOf<GridRow>()
|
||||||
|
|
||||||
// 第1行:日期 | 航班号
|
// 第1行:日期 | 航班号
|
||||||
@@ -345,7 +394,7 @@ object PrinterUtils {
|
|||||||
merged = CellData("备注:", "NOTES:", bean.remark)
|
merged = CellData("备注:", "NOTES:", bean.remark)
|
||||||
))
|
))
|
||||||
|
|
||||||
printMergedGrid(rows)
|
return rows
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun printCommonGrid(rowItem: ArrayList<List<String>>) {
|
private fun printCommonGrid(rowItem: ArrayList<List<String>>) {
|
||||||
@@ -456,6 +505,13 @@ object PrinterUtils {
|
|||||||
* 打印支持合并单元格的表格
|
* 打印支持合并单元格的表格
|
||||||
*/
|
*/
|
||||||
private fun printMergedGrid(rows: ArrayList<GridRow>) {
|
private fun printMergedGrid(rows: ArrayList<GridRow>) {
|
||||||
|
portManager?.writeDataImmediately(buildMergedGridBytes(rows))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成支持合并单元格的表格标签字节流(自包含一张完整标签的 TSPL 指令)
|
||||||
|
*/
|
||||||
|
private fun buildMergedGridBytes(rows: ArrayList<GridRow>): ByteArray {
|
||||||
val gridStartX = 30
|
val gridStartX = 30
|
||||||
val titleTopMargin = 50 // 标题顶部间距
|
val titleTopMargin = 50 // 标题顶部间距
|
||||||
val titleFontSize = 130 // 标题字体大小
|
val titleFontSize = 130 // 标题字体大小
|
||||||
@@ -536,7 +592,7 @@ object PrinterUtils {
|
|||||||
addPrint(1)
|
addPrint(1)
|
||||||
}.bytes
|
}.bytes
|
||||||
|
|
||||||
portManager?.writeDataImmediately(bytes)
|
return bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,6 +34,13 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:screenOrientation="unspecified" />
|
android:screenOrientation="unspecified" />
|
||||||
|
|
||||||
|
<!-- 海关回执(只读) -->
|
||||||
|
<activity
|
||||||
|
android:name=".ui.page.customs.CustomsResponseActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="unspecified" />
|
||||||
|
|
||||||
<!-- 屏幕适配 -->
|
<!-- 屏幕适配 -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="design_width_in_dp"
|
android:name="design_width_in_dp"
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 输入框边框 + 与 Spinner(bg_spinner_pda_new)完全一致的下拉箭头,用于“手动输入 + 下拉选择”组合输入框 -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/bg_data_layout_new" />
|
||||||
|
<item
|
||||||
|
android:width="8.7dp"
|
||||||
|
android:height="6dp"
|
||||||
|
android:drawable="@drawable/img_sp_down"
|
||||||
|
android:gravity="center_vertical|right"
|
||||||
|
android:right="10dp" />
|
||||||
|
</layer-list>
|
||||||
6
module_base/src/main/res/drawable/bg_phone_fab_red.xml
Normal file
6
module_base/src/main/res/drawable/bg_phone_fab_red.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 手机版圆形操作按钮:红色(出港运抵卡片长按菜单「删除申报」) -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="@color/phone_danger_text" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<data />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/color_f2"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<include layout="@layout/title_tool_bar" />
|
||||||
|
|
||||||
|
<!-- 回执内容区域(只读,可滚动、可长按复制) -->
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etContent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:background="@drawable/bg_white_radius_8"
|
||||||
|
android:gravity="top|start"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:overScrollMode="always"
|
||||||
|
android:padding="20dp"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
海关回执详情页(手机版,5.5 寸手持端):只读回执文本卡片。
|
||||||
|
平板变体:layout-sw600dp/activity_customs_response.xml,两者共用 CustomsResponseActivity 与 etContent。
|
||||||
|
-->
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<data />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/phone_page_bg"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<include layout="@layout/title_tool_bar" />
|
||||||
|
|
||||||
|
<!-- 回执内容区域(只读,可滚动、可长按复制) -->
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etContent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="12dp"
|
||||||
|
android:background="@drawable/bg_phone_card"
|
||||||
|
android:gravity="top|start"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:overScrollMode="always"
|
||||||
|
android:padding="14dp"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:textColor="@color/phone_text_body"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<!--
|
<!--
|
||||||
PhoneBottomBar 内部布局(手机版底部操作条)
|
PhoneBottomBar 内部布局(手机版底部操作条)
|
||||||
|
|
||||||
结构:全选 + 已选统计 + 主操作按钮
|
结构:全选 + 已选统计 + 次操作按钮(可选,最多 2 个,未设置文字时隐藏)+ 主操作按钮
|
||||||
对应设计稿各多选列表页底部的白色操作条
|
对应设计稿各多选列表页底部的白色操作条
|
||||||
-->
|
-->
|
||||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
@@ -48,6 +48,34 @@
|
|||||||
android:textColor="@color/phone_text_hint"
|
android:textColor="@color/phone_text_hint"
|
||||||
android:textSize="12sp" />
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<!-- 次操作按钮 1(浅蓝弱按钮,subAction1Text 为空时隐藏) -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_sub_action1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="@drawable/bg_phone_btn_weak"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="12dp"
|
||||||
|
android:paddingVertical="9dp"
|
||||||
|
android:textColor="@color/phone_primary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- 次操作按钮 2(浅蓝弱按钮,subAction2Text 为空时隐藏) -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_sub_action2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="@drawable/bg_phone_btn_weak"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="12dp"
|
||||||
|
android:paddingVertical="9dp"
|
||||||
|
android:textColor="@color/phone_primary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<!-- 主操作按钮 -->
|
<!-- 主操作按钮 -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_action"
|
android:id="@+id/tv_action"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class GjcAssembleWeightEditActivity :
|
|||||||
override fun viewModelClass() = GjcAssembleWeightEditViewModel::class.java
|
override fun viewModelClass() = GjcAssembleWeightEditViewModel::class.java
|
||||||
|
|
||||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||||
setBackArrow("国际出港修改组装重量")
|
setBackArrow("国际出港重量回填")
|
||||||
|
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ class GjcQueryActivity :
|
|||||||
if (visible) showFilterPanel() else hideFilterPanel()
|
if (visible) showFilterPanel() else hideFilterPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 航班号:大写字母+数字
|
// 航班号(搜索栏):大写字母+数字
|
||||||
padFilterPanel.filterFlightNo.et.setUpperCaseAlphanumericFilter()
|
binding.flightNoSearch?.et?.setUpperCaseAlphanumericFilter()
|
||||||
// 目的港:仅大写字母
|
// 目的港:仅大写字母
|
||||||
padFilterPanel.filterDest.et.setUpperCaseLetterFilter()
|
padFilterPanel.filterDest.et.setUpperCaseLetterFilter()
|
||||||
} else {
|
} else {
|
||||||
@@ -67,11 +67,6 @@ class GjcQueryActivity :
|
|||||||
binding.phoneFilterDest?.et?.setUpperCaseLetterFilter()
|
binding.phoneFilterDest?.et?.setUpperCaseLetterFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 手机形态专属:特码下拉字典 + 已入库/已离港统计
|
|
||||||
if (DeviceUtil.isPhone()) {
|
|
||||||
viewModel.initPhoneExtras()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始加载
|
// 初始加载
|
||||||
viewModel.refresh()
|
viewModel.refresh()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,6 @@ class GjcWeighingRecordListActivity :
|
|||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
binding.activity = this
|
binding.activity = this
|
||||||
|
|
||||||
// 手机版专属数据:放开默认日期、加载计重人下拉
|
|
||||||
// 平板端不调用,查询链路与请求次数与改造前完全一致
|
|
||||||
if (DeviceUtil.isPhone()) {
|
|
||||||
viewModel.initPhoneExtras()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化代理人列表
|
// 初始化代理人列表
|
||||||
viewModel.initAgentList()
|
viewModel.initAgentList()
|
||||||
|
|
||||||
@@ -76,6 +70,7 @@ class GjcWeighingRecordListActivity :
|
|||||||
/** 手机版筛选:重置(只清弹层内条件,不影响顶部搜索框) */
|
/** 手机版筛选:重置(只清弹层内条件,不影响顶部搜索框) */
|
||||||
fun resetFilter() {
|
fun resetFilter() {
|
||||||
viewModel.flightDate.value = ""
|
viewModel.flightDate.value = ""
|
||||||
|
viewModel.flightNo.value = ""
|
||||||
viewModel.agentCode.value = ""
|
viewModel.agentCode.value = ""
|
||||||
viewModel.spCode.value = ""
|
viewModel.spCode.value = ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,11 +34,6 @@ class GjcWeighingStartActivity :
|
|||||||
setBackArrow(if (DeviceUtil.isPhone()) "开始计重" else "国际出港开始计重")
|
setBackArrow(if (DeviceUtil.isPhone()) "开始计重" else "国际出港开始计重")
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
// 手机版专属:提交体携带托盘数量/托盘自重(平板不调用,请求体与改造前完全一致)
|
|
||||||
if (DeviceUtil.isPhone()) {
|
|
||||||
viewModel.initPhoneExtras()
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.initOnCreated(this, intent)
|
viewModel.initOnCreated(this, intent)
|
||||||
|
|
||||||
setupFloatButtons()
|
setupFloatButtons()
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ class IntExpArriveActivity :
|
|||||||
fun resetFilter() {
|
fun resetFilter() {
|
||||||
viewModel.flightDate.value = ""
|
viewModel.flightDate.value = ""
|
||||||
viewModel.flightNo.value = ""
|
viewModel.flightNo.value = ""
|
||||||
|
viewModel.hno.value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 手机版筛选:确认 */
|
/** 手机版筛选:确认 */
|
||||||
@@ -142,12 +143,11 @@ class IntExpArriveActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 进入「回执」页(手机专属只读页):复用 haWbList[].response + arrivalOpDate,零新增请求。
|
* 浮层「回执」:与平板侧滑「回执」同链路(getResponse 查询海关回执 → 共用只读回执页)。
|
||||||
*/
|
*/
|
||||||
fun openReceipt(bean: GjcMaWb) {
|
fun openReceipt(bean: GjcMaWb) {
|
||||||
bean.actionMenuVisible.set(false)
|
bean.actionMenuVisible.set(false)
|
||||||
val list = ArrayList<GjcHaWb>(bean.haWbList.orEmpty())
|
viewModel.queryCustomsResponse(bean)
|
||||||
IntExpArriveReceiptActivity.start(this, bean.wbNo.orEmpty(), list)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.lukouguoji.gjc.databinding.ActivityIntExpStorageUseBinding
|
|||||||
import com.lukouguoji.gjc.dialog.IntExpMoveClearDialogModel
|
import com.lukouguoji.gjc.dialog.IntExpMoveClearDialogModel
|
||||||
import com.lukouguoji.gjc.dialog.IntExpModifyStorageDialogModel
|
import com.lukouguoji.gjc.dialog.IntExpModifyStorageDialogModel
|
||||||
import com.lukouguoji.gjc.dialog.IntExpInStorageDialogModel
|
import com.lukouguoji.gjc.dialog.IntExpInStorageDialogModel
|
||||||
|
import com.lukouguoji.gjc.dialog.IntExpOutStorageDialogModel
|
||||||
import com.lukouguoji.gjc.viewModel.IntExpStorageUseViewModel
|
import com.lukouguoji.gjc.viewModel.IntExpStorageUseViewModel
|
||||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||||
import com.lukouguoji.module_base.common.Constant
|
import com.lukouguoji.module_base.common.Constant
|
||||||
@@ -17,7 +18,6 @@ import com.lukouguoji.module_base.impl.FlowBus
|
|||||||
import com.lukouguoji.module_base.impl.observe
|
import com.lukouguoji.module_base.impl.observe
|
||||||
import com.lukouguoji.module_base.ktx.commonAdapter
|
import com.lukouguoji.module_base.ktx.commonAdapter
|
||||||
import com.lukouguoji.module_base.ktx.showToast
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
import com.lukouguoji.module_base.model.ConfirmDialogModel
|
|
||||||
import com.lukouguoji.module_base.router.ARouterConstants
|
import com.lukouguoji.module_base.router.ARouterConstants
|
||||||
import com.lukouguoji.module_base.util.DeviceUtil
|
import com.lukouguoji.module_base.util.DeviceUtil
|
||||||
|
|
||||||
@@ -83,7 +83,8 @@ class IntExpStorageUseActivity :
|
|||||||
IntExpMoveClearDialogModel { dialog ->
|
IntExpMoveClearDialogModel { dialog ->
|
||||||
// 用户点击保存后,执行清仓操作
|
// 用户点击保存后,执行清仓操作
|
||||||
val clearNormal = dialog.clearNormal.value ?: ""
|
val clearNormal = dialog.clearNormal.value ?: ""
|
||||||
viewModel.performClear(clearNormal, maWbListForClear)
|
val clearRemark = dialog.clearRemark.value ?: ""
|
||||||
|
viewModel.performClear(clearNormal, clearRemark, maWbListForClear)
|
||||||
}.show(this)
|
}.show(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +125,7 @@ class IntExpStorageUseActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 显示出库二次确认对话框
|
* 显示出库操作对话框
|
||||||
*/
|
*/
|
||||||
fun showOutStorageDialog() {
|
fun showOutStorageDialog() {
|
||||||
val list = viewModel.pageModel.rv?.commonAdapter()?.items as? List<*> ?: return
|
val list = viewModel.pageModel.rv?.commonAdapter()?.items as? List<*> ?: return
|
||||||
@@ -142,24 +143,33 @@ class IntExpStorageUseActivity :
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示二次确认对话框
|
// 显示出库对话框,件数/重量默认取选中库位的入库件数/重量
|
||||||
ConfirmDialogModel(
|
IntExpOutStorageDialogModel(
|
||||||
message = "确定要将选中的 ${selectedStorageUseList.size} 个库位执行出库操作吗?",
|
storageCount = selectedStorageUseList.size,
|
||||||
title = "出库确认"
|
defaultPc = selectedStorageUseList.mapNotNull { it.inPc }.takeIf { it.isNotEmpty() }?.sum(),
|
||||||
) {
|
defaultWeight = selectedStorageUseList.mapNotNull { it.inWeight }.takeIf { it.isNotEmpty() }?.sum()
|
||||||
// 用户确认后,执行出库操作
|
) { dialog ->
|
||||||
viewModel.performOutStorage(selectedStorageUseList)
|
// 用户点击确定后,执行出库操作
|
||||||
|
viewModel.performOutStorage(
|
||||||
|
dialog.pc.value?.toLongOrNull(),
|
||||||
|
dialog.weight.value?.toDoubleOrNull(),
|
||||||
|
selectedStorageUseList
|
||||||
|
)
|
||||||
}.show(this)
|
}.show(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 5.5寸手机版:卡片级操作 ====================
|
// ==================== 5.5寸手机版:卡片级操作 ====================
|
||||||
// 语义等价于「勾选该主单 + 其全部库位」再执行平板的批量操作,复用 performXxx 请求链路。
|
// 语义等价于「勾选该主单 + 其全部库位」再执行平板的批量操作,弹框与请求链路完全复用平板。
|
||||||
|
|
||||||
/** 卡片「清仓」(已清仓的卡片按钮置灰,ViewHolder 已拦截) */
|
/** 卡片「清仓」(已清仓的卡片按钮置灰,ViewHolder 已拦截) */
|
||||||
fun cardClear(bean: com.lukouguoji.module_base.bean.GjcMaWb) {
|
fun cardClear(bean: com.lukouguoji.module_base.bean.GjcMaWb) {
|
||||||
val maWbListForClear = listOf(bean.copy(storageUseList = bean.storageUseList ?: emptyList()))
|
val maWbListForClear = listOf(bean.copy(storageUseList = bean.storageUseList ?: emptyList()))
|
||||||
IntExpMoveClearDialogModel { dialog ->
|
IntExpMoveClearDialogModel { dialog ->
|
||||||
viewModel.performClear(dialog.clearNormal.value ?: "", maWbListForClear)
|
viewModel.performClear(
|
||||||
|
dialog.clearNormal.value ?: "",
|
||||||
|
dialog.clearRemark.value ?: "",
|
||||||
|
maWbListForClear
|
||||||
|
)
|
||||||
}.show(this)
|
}.show(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,26 +186,40 @@ class IntExpStorageUseActivity :
|
|||||||
}.show(this)
|
}.show(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 卡片「出库」:该运单全部库位出库(与平板勾选主单后出库同语义) */
|
/** 卡片「出库」:该运单全部库位出库(与平板勾选主单后出库同语义,件数/重量默认取库位入库件数/重量) */
|
||||||
fun cardOutStorage(bean: com.lukouguoji.module_base.bean.GjcMaWb) {
|
fun cardOutStorage(bean: com.lukouguoji.module_base.bean.GjcMaWb) {
|
||||||
val storageList = bean.storageUseList ?: emptyList()
|
val storageList = bean.storageUseList ?: emptyList()
|
||||||
if (storageList.isEmpty()) {
|
if (storageList.isEmpty()) {
|
||||||
showToast("该运单暂无库位")
|
showToast("该运单暂无库位")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ConfirmDialogModel(
|
IntExpOutStorageDialogModel(
|
||||||
message = "确定要将运单 ${bean.wbNo} 的 ${storageList.size} 个库位执行出库操作吗?",
|
storageCount = storageList.size,
|
||||||
title = "出库确认"
|
defaultPc = storageList.mapNotNull { it.inPc }.takeIf { it.isNotEmpty() }?.sum(),
|
||||||
) {
|
defaultWeight = storageList.mapNotNull { it.inWeight }.takeIf { it.isNotEmpty() }?.sum()
|
||||||
viewModel.performOutStorage(storageList)
|
) { dialog ->
|
||||||
|
viewModel.performOutStorage(
|
||||||
|
dialog.pc.value?.toLongOrNull(),
|
||||||
|
dialog.weight.value?.toDoubleOrNull(),
|
||||||
|
storageList
|
||||||
|
)
|
||||||
}.show(this)
|
}.show(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 卡片「入库」:弹层选库位后整票入库 */
|
/** 卡片「入库」:弹层选库位、填件数重量后整票入库 */
|
||||||
fun cardInStorage(bean: com.lukouguoji.module_base.bean.GjcMaWb) {
|
fun cardInStorage(bean: com.lukouguoji.module_base.bean.GjcMaWb) {
|
||||||
val maWbListForInStorage = listOf(bean.copy(storageUseList = bean.storageUseList ?: emptyList()))
|
val maWbListForInStorage = listOf(bean.copy(storageUseList = bean.storageUseList ?: emptyList()))
|
||||||
IntExpInStorageDialogModel { dialog ->
|
IntExpInStorageDialogModel(
|
||||||
viewModel.performInStorage(dialog.locationName, dialog.locationId, maWbListForInStorage)
|
defaultPc = bean.pc,
|
||||||
|
defaultWeight = bean.weight
|
||||||
|
) { dialog ->
|
||||||
|
viewModel.performInStorage(
|
||||||
|
dialog.locationName,
|
||||||
|
dialog.locationId,
|
||||||
|
dialog.pc.value?.toLongOrNull(),
|
||||||
|
dialog.weight.value?.toDoubleOrNull(),
|
||||||
|
maWbListForInStorage
|
||||||
|
)
|
||||||
}.show(this)
|
}.show(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,12 +249,19 @@ class IntExpStorageUseActivity :
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示入库对话框
|
// 显示入库对话框,件数/重量默认取选中运单的件数/重量
|
||||||
IntExpInStorageDialogModel { dialog ->
|
IntExpInStorageDialogModel(
|
||||||
|
defaultPc = maWbListForInStorage.mapNotNull { it.pc }.takeIf { it.isNotEmpty() }?.sum(),
|
||||||
|
defaultWeight = maWbListForInStorage.mapNotNull { it.weight }.takeIf { it.isNotEmpty() }?.sum()
|
||||||
|
) { dialog ->
|
||||||
// 用户点击保存后,执行入库操作
|
// 用户点击保存后,执行入库操作
|
||||||
val locationName = dialog.locationName
|
viewModel.performInStorage(
|
||||||
val locationId = dialog.locationId
|
dialog.locationName,
|
||||||
viewModel.performInStorage(locationName, locationId, maWbListForInStorage)
|
dialog.locationId,
|
||||||
|
dialog.pc.value?.toLongOrNull(),
|
||||||
|
dialog.weight.value?.toDoubleOrNull(),
|
||||||
|
maWbListForInStorage
|
||||||
|
)
|
||||||
}.show(this)
|
}.show(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,19 @@ import com.lukouguoji.gjc.R
|
|||||||
import com.lukouguoji.gjc.databinding.DialogIntExpArriveDeleteBinding
|
import com.lukouguoji.gjc.databinding.DialogIntExpArriveDeleteBinding
|
||||||
import com.lukouguoji.module_base.base.BaseDialogModel
|
import com.lukouguoji.module_base.base.BaseDialogModel
|
||||||
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
|
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
|
||||||
|
import com.lukouguoji.module_base.util.DeviceUtil
|
||||||
import dev.utils.app.info.KeyValue
|
import dev.utils.app.info.KeyValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港运抵 - 删除申报对话框
|
* 国际出港运抵 - 删除申报对话框
|
||||||
|
* 手机为底部弹层(layout/ 变体),平板为居中弹框(layout-sw600dp/ 变体)
|
||||||
*/
|
*/
|
||||||
class IntExpArriveDeleteDialogModel(
|
class IntExpArriveDeleteDialogModel(
|
||||||
val changeReasonList: List<KeyValue>, // 变更原因列表
|
val changeReasonList: List<KeyValue>, // 变更原因列表
|
||||||
private val callback: (IntExpArriveDeleteDialogModel) -> Unit
|
private val callback: (IntExpArriveDeleteDialogModel) -> Unit
|
||||||
) : BaseDialogModel<DialogIntExpArriveDeleteBinding>(DIALOG_TYPE_CENTER) {
|
) : BaseDialogModel<DialogIntExpArriveDeleteBinding>(
|
||||||
|
if (DeviceUtil.isPhone()) DIALOG_TYPE_BOTTOM else DIALOG_TYPE_CENTER
|
||||||
|
) {
|
||||||
|
|
||||||
// 变更原因代码(存储的是 code)
|
// 变更原因代码(存储的是 code)
|
||||||
val changeReason = MutableLiveData("")
|
val changeReason = MutableLiveData("")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.lukouguoji.gjc.dialog
|
package com.lukouguoji.gjc.dialog
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.text.InputType
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.lukouguoji.gjc.R
|
import com.lukouguoji.gjc.R
|
||||||
import com.lukouguoji.gjc.databinding.DialogIntExpInStorageBinding
|
import com.lukouguoji.gjc.databinding.DialogIntExpInStorageBinding
|
||||||
@@ -8,15 +9,20 @@ import com.lukouguoji.module_base.base.BaseDialogModel
|
|||||||
import com.lukouguoji.module_base.http.net.NetApply
|
import com.lukouguoji.module_base.http.net.NetApply
|
||||||
import com.lukouguoji.module_base.ktx.launchCollect
|
import com.lukouguoji.module_base.ktx.launchCollect
|
||||||
import com.lukouguoji.module_base.ktx.showToast
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
|
import com.lukouguoji.module_base.ktx.toPlainText
|
||||||
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
|
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
|
||||||
import com.lukouguoji.module_base.util.DeviceUtil
|
import com.lukouguoji.module_base.util.DeviceUtil
|
||||||
import dev.utils.app.info.KeyValue
|
import dev.utils.app.info.KeyValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港仓库 - 入库操作对话框
|
* 国际出港仓库 - 入库操作对话框
|
||||||
|
* @param defaultPc 默认件数(选中运单的件数)
|
||||||
|
* @param defaultWeight 默认重量(选中运单的重量)
|
||||||
* 手机为底部弹层(layout/ 变体),平板为居中弹框(layout-sw600dp/ 变体)
|
* 手机为底部弹层(layout/ 变体),平板为居中弹框(layout-sw600dp/ 变体)
|
||||||
*/
|
*/
|
||||||
class IntExpInStorageDialogModel(
|
class IntExpInStorageDialogModel(
|
||||||
|
defaultPc: Long?,
|
||||||
|
defaultWeight: Double?,
|
||||||
private val callback: (IntExpInStorageDialogModel) -> Unit
|
private val callback: (IntExpInStorageDialogModel) -> Unit
|
||||||
) : BaseDialogModel<DialogIntExpInStorageBinding>(
|
) : BaseDialogModel<DialogIntExpInStorageBinding>(
|
||||||
if (DeviceUtil.isPhone()) DIALOG_TYPE_BOTTOM else DIALOG_TYPE_CENTER
|
if (DeviceUtil.isPhone()) DIALOG_TYPE_BOTTOM else DIALOG_TYPE_CENTER
|
||||||
@@ -34,12 +40,25 @@ class IntExpInStorageDialogModel(
|
|||||||
// 库位名称 (后端需要的name)
|
// 库位名称 (后端需要的name)
|
||||||
var locationName: String = ""
|
var locationName: String = ""
|
||||||
|
|
||||||
|
// 入库件数
|
||||||
|
val pc = MutableLiveData(defaultPc?.toString() ?: "")
|
||||||
|
|
||||||
|
// 入库重量
|
||||||
|
val weight = MutableLiveData(defaultWeight?.toPlainText() ?: "")
|
||||||
|
|
||||||
override fun layoutId(): Int {
|
override fun layoutId(): Int {
|
||||||
return R.layout.dialog_int_exp_in_storage
|
return R.layout.dialog_int_exp_in_storage
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDialogCreated(context: Context) {
|
override fun onDialogCreated(context: Context) {
|
||||||
binding.model = this
|
binding.model = this
|
||||||
|
// 平板变体 pc_input/weight_input,手机变体 pc_input_phone/weight_input_phone,单侧 id 在 Binding 中可空
|
||||||
|
val pcType = InputType.TYPE_CLASS_NUMBER
|
||||||
|
val weightType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||||
|
binding.pcInput?.et?.inputType = pcType
|
||||||
|
binding.weightInput?.et?.inputType = weightType
|
||||||
|
binding.pcInputPhone?.et?.inputType = pcType
|
||||||
|
binding.weightInputPhone?.et?.inputType = weightType
|
||||||
loadLocationList()
|
loadLocationList()
|
||||||
|
|
||||||
// 监听选择变化,更新locationId和locationName
|
// 监听选择变化,更新locationId和locationName
|
||||||
@@ -72,6 +91,14 @@ class IntExpInStorageDialogModel(
|
|||||||
if (selectedLocationCode.value.verifyNullOrEmpty("请选择库位")) {
|
if (selectedLocationCode.value.verifyNullOrEmpty("请选择库位")) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (pc.value?.toLongOrNull() == null) {
|
||||||
|
showToast("请输入正确的件数")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (weight.value?.toDoubleOrNull() == null) {
|
||||||
|
showToast("请输入正确的重量")
|
||||||
|
return
|
||||||
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
callback(this)
|
callback(this)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ class IntExpMoveClearDialogModel(
|
|||||||
// 清仓正常(存储的是 code:"0" 或 "1")
|
// 清仓正常(存储的是 code:"0" 或 "1")
|
||||||
val clearNormal = MutableLiveData("")
|
val clearNormal = MutableLiveData("")
|
||||||
|
|
||||||
|
// 清仓备注(选填)
|
||||||
|
val clearRemark = MutableLiveData("")
|
||||||
|
|
||||||
// 清仓正常选项列表
|
// 清仓正常选项列表
|
||||||
val clearNormalList = MutableLiveData<List<KeyValue>>().apply {
|
val clearNormalList = MutableLiveData<List<KeyValue>>().apply {
|
||||||
value = listOf(
|
value = listOf(
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.lukouguoji.gjc.dialog
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.InputType
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import com.lukouguoji.gjc.R
|
||||||
|
import com.lukouguoji.gjc.databinding.DialogIntExpOutStorageBinding
|
||||||
|
import com.lukouguoji.module_base.base.BaseDialogModel
|
||||||
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
|
import com.lukouguoji.module_base.ktx.toPlainText
|
||||||
|
import com.lukouguoji.module_base.util.DeviceUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际出港仓库 - 出库操作对话框
|
||||||
|
* 手机为底部弹层(layout/ 变体),平板为居中弹框(layout-sw600dp/ 变体)
|
||||||
|
* @param storageCount 选中的库位数
|
||||||
|
* @param defaultPc 默认件数(选中库位的入库件数)
|
||||||
|
* @param defaultWeight 默认重量(选中库位的入库重量)
|
||||||
|
*/
|
||||||
|
class IntExpOutStorageDialogModel(
|
||||||
|
storageCount: Int,
|
||||||
|
defaultPc: Long?,
|
||||||
|
defaultWeight: Double?,
|
||||||
|
private val callback: (IntExpOutStorageDialogModel) -> Unit
|
||||||
|
) : BaseDialogModel<DialogIntExpOutStorageBinding>(
|
||||||
|
if (DeviceUtil.isPhone()) DIALOG_TYPE_BOTTOM else DIALOG_TYPE_CENTER
|
||||||
|
) {
|
||||||
|
|
||||||
|
val tip = "已选择 $storageCount 个库位执行出库操作"
|
||||||
|
|
||||||
|
// 出库件数
|
||||||
|
val pc = MutableLiveData(defaultPc?.toString() ?: "")
|
||||||
|
|
||||||
|
// 出库重量
|
||||||
|
val weight = MutableLiveData(defaultWeight?.toPlainText() ?: "")
|
||||||
|
|
||||||
|
override fun layoutId(): Int {
|
||||||
|
return R.layout.dialog_int_exp_out_storage
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDialogCreated(context: Context) {
|
||||||
|
binding.model = this
|
||||||
|
// 平板变体 pc_input/weight_input,手机变体 pc_input_phone/weight_input_phone,单侧 id 在 Binding 中可空
|
||||||
|
val pcType = InputType.TYPE_CLASS_NUMBER
|
||||||
|
val weightType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||||
|
binding.pcInput?.et?.inputType = pcType
|
||||||
|
binding.weightInput?.et?.inputType = weightType
|
||||||
|
binding.pcInputPhone?.et?.inputType = pcType
|
||||||
|
binding.weightInputPhone?.et?.inputType = weightType
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确定按钮点击
|
||||||
|
*/
|
||||||
|
fun onSaveClick() {
|
||||||
|
if (pc.value?.toLongOrNull() == null) {
|
||||||
|
showToast("请输入正确的件数")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (weight.value?.toDoubleOrNull() == null) {
|
||||||
|
showToast("请输入正确的重量")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
callback(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,14 +9,18 @@ import com.lukouguoji.module_base.http.net.NetApply
|
|||||||
import com.lukouguoji.module_base.ktx.launchCollect
|
import com.lukouguoji.module_base.ktx.launchCollect
|
||||||
import com.lukouguoji.module_base.ktx.showToast
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
|
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
|
||||||
|
import com.lukouguoji.module_base.util.DeviceUtil
|
||||||
import dev.utils.app.info.KeyValue
|
import dev.utils.app.info.KeyValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港出库交接 - 待配运操作对话框
|
* 国际出港出库交接 - 待配运操作对话框
|
||||||
|
* 手机为底部弹层(layout/ 变体),平板为居中弹框(layout-sw600dp/ 变体)
|
||||||
*/
|
*/
|
||||||
class IntExpOutWaitingTransDialogModel(
|
class IntExpOutWaitingTransDialogModel(
|
||||||
private val callback: (IntExpOutWaitingTransDialogModel) -> Unit
|
private val callback: (IntExpOutWaitingTransDialogModel) -> Unit
|
||||||
) : BaseDialogModel<DialogIntExpOutWaitingTransBinding>(DIALOG_TYPE_CENTER) {
|
) : BaseDialogModel<DialogIntExpOutWaitingTransBinding>(
|
||||||
|
if (DeviceUtil.isPhone()) DIALOG_TYPE_BOTTOM else DIALOG_TYPE_CENTER
|
||||||
|
) {
|
||||||
|
|
||||||
// 库位列表
|
// 库位列表
|
||||||
val locationList = MutableLiveData<List<KeyValue>>()
|
val locationList = MutableLiveData<List<KeyValue>>()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.view.View
|
|||||||
import com.lukouguoji.gjc.activity.GjcWeighingRecordDetailsActivity
|
import com.lukouguoji.gjc.activity.GjcWeighingRecordDetailsActivity
|
||||||
import com.lukouguoji.gjc.activity.GjcWeighingWbDetailActivity
|
import com.lukouguoji.gjc.activity.GjcWeighingWbDetailActivity
|
||||||
import com.lukouguoji.gjc.databinding.ItemGjcWeighingRecordBinding
|
import com.lukouguoji.gjc.databinding.ItemGjcWeighingRecordBinding
|
||||||
|
import com.lukouguoji.gjc.viewModel.GjcWeighingRecordViewModel
|
||||||
import com.lukouguoji.module_base.base.BaseViewHolder
|
import com.lukouguoji.module_base.base.BaseViewHolder
|
||||||
import com.lukouguoji.module_base.bean.GjcWeighingRecordBean
|
import com.lukouguoji.module_base.bean.GjcWeighingRecordBean
|
||||||
|
|
||||||
@@ -36,6 +37,11 @@ class GjcWeighingRecordViewHolder(view: View) :
|
|||||||
GjcWeighingRecordDetailsActivity.start(it.context, bean)
|
GjcWeighingRecordDetailsActivity.start(it.context, bean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 「运抵申报」携带运单号进入出港运抵(手机独有入口,对应平板勾选单条后的「运抵申报」)
|
||||||
|
binding.tvDeclare?.setOnClickListener {
|
||||||
|
GjcWeighingRecordViewModel.openArriveDeclare(bean.wbNo)
|
||||||
|
}
|
||||||
|
|
||||||
// 「查看详情」进运单详情(手机独有页面)
|
// 「查看详情」进运单详情(手机独有页面)
|
||||||
binding.tvDetail?.setOnClickListener {
|
binding.tvDetail?.setOnClickListener {
|
||||||
GjcWeighingWbDetailActivity.start(it.context, bean)
|
GjcWeighingWbDetailActivity.start(it.context, bean)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.lukouguoji.module_base.common.ConstantEvent
|
|||||||
import com.lukouguoji.module_base.impl.FlowBus
|
import com.lukouguoji.module_base.impl.FlowBus
|
||||||
import com.lukouguoji.module_base.bean.GjcMaWb
|
import com.lukouguoji.module_base.bean.GjcMaWb
|
||||||
import com.lukouguoji.module_base.ktx.refresh
|
import com.lukouguoji.module_base.ktx.refresh
|
||||||
|
import com.lukouguoji.module_base.ktx.ITEM_CLICK_TYPE_CUSTOMS_RESPONSE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港-出港运抵 列表项ViewHolder
|
* 国际出港-出港运抵 列表项ViewHolder
|
||||||
@@ -30,6 +31,12 @@ class IntExpArriveViewHolder(view: View) :
|
|||||||
binding.viewModel = activity?.itemViewModel
|
binding.viewModel = activity?.itemViewModel
|
||||||
binding.executePendingBindings()
|
binding.executePendingBindings()
|
||||||
|
|
||||||
|
// 侧滑菜单 - 回执按钮(关闭侧滑后分发到 ViewModel,平板独有)
|
||||||
|
binding.btnResponse?.setOnClickListener {
|
||||||
|
binding.swipeMenu?.quickClose()
|
||||||
|
clickListener?.onItemClick(position, ITEM_CLICK_TYPE_CUSTOMS_RESPONSE)
|
||||||
|
}
|
||||||
|
|
||||||
// 添加图标点击事件 - 切换选择状态(平板独有)
|
// 添加图标点击事件 - 切换选择状态(平板独有)
|
||||||
binding.ivIcon?.setOnClickListener {
|
binding.ivIcon?.setOnClickListener {
|
||||||
bean.checked.set(!bean.checked.get())
|
bean.checked.set(!bean.checked.get())
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.lukouguoji.module_base.base.BaseViewHolder
|
|||||||
import com.lukouguoji.module_base.common.ConstantEvent
|
import com.lukouguoji.module_base.common.ConstantEvent
|
||||||
import com.lukouguoji.module_base.impl.FlowBus
|
import com.lukouguoji.module_base.impl.FlowBus
|
||||||
import com.lukouguoji.module_base.bean.GjcExportLoad
|
import com.lukouguoji.module_base.bean.GjcExportLoad
|
||||||
|
import com.lukouguoji.module_base.ktx.ITEM_CLICK_TYPE_CUSTOMS_RESPONSE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港-出港装载 列表项ViewHolder
|
* 国际出港-出港装载 列表项ViewHolder
|
||||||
@@ -19,6 +20,12 @@ class IntExpLoadViewHolder(view: View) :
|
|||||||
binding.position = position
|
binding.position = position
|
||||||
binding.executePendingBindings()
|
binding.executePendingBindings()
|
||||||
|
|
||||||
|
// 侧滑菜单 - 回执按钮(关闭侧滑后分发到 ViewModel)
|
||||||
|
binding.btnResponse.setOnClickListener {
|
||||||
|
binding.swipeMenu.quickClose()
|
||||||
|
clickListener?.onItemClick(position, ITEM_CLICK_TYPE_CUSTOMS_RESPONSE)
|
||||||
|
}
|
||||||
|
|
||||||
// 添加图标点击事件 - 切换选择状态
|
// 添加图标点击事件 - 切换选择状态
|
||||||
binding.ivIcon.setOnClickListener {
|
binding.ivIcon.setOnClickListener {
|
||||||
// 反转checked状态
|
// 反转checked状态
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.lukouguoji.gjc.holder
|
|||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.lukouguoji.gjc.databinding.ItemIntExpMoveBinding
|
import com.lukouguoji.gjc.databinding.ItemIntExpMoveBinding
|
||||||
import com.lukouguoji.gjc.page.move.IntExpMoveDetailActivity
|
import com.lukouguoji.gjc.page.move.IntExpMoveEditActivity
|
||||||
import com.lukouguoji.module_base.base.BaseViewHolder
|
import com.lukouguoji.module_base.base.BaseViewHolder
|
||||||
import com.lukouguoji.module_base.bean.GjcMove
|
import com.lukouguoji.module_base.bean.GjcMove
|
||||||
import com.lukouguoji.module_base.common.ConstantEvent
|
import com.lukouguoji.module_base.common.ConstantEvent
|
||||||
@@ -21,9 +21,12 @@ class IntExpMoveViewHolder(view: View) :
|
|||||||
binding.bean = bean
|
binding.bean = bean
|
||||||
binding.position = position
|
binding.position = position
|
||||||
|
|
||||||
// 手机版「查看详情」→ 运单详情页(平板变体无此控件,为 null 时跳过)
|
// 手机版「查看详情」/「编辑」→ 与平板点击卡片 / 侧滑编辑同一页面(平板变体无此控件,为 null 时跳过)
|
||||||
binding.llDetail?.setOnClickListener {
|
binding.llDetail?.setOnClickListener {
|
||||||
IntExpMoveDetailActivity.start(itemView.context, bean)
|
IntExpMoveEditActivity.startForDetails(itemView.context, bean)
|
||||||
|
}
|
||||||
|
binding.tvEdit?.setOnClickListener {
|
||||||
|
IntExpMoveEditActivity.startForEdit(itemView.context, bean)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加图标点击事件 - 切换选择状态
|
// 添加图标点击事件 - 切换选择状态
|
||||||
@@ -37,6 +40,17 @@ class IntExpMoveViewHolder(view: View) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 点击列表项进入移库详情
|
||||||
|
binding.ll?.setOnClickListener {
|
||||||
|
IntExpMoveEditActivity.startForDetails(itemView.context, bean)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 侧滑菜单 - 编辑按钮(平板独有)
|
||||||
|
binding.btnEdit?.setOnClickListener {
|
||||||
|
binding.swipeMenu?.quickClose()
|
||||||
|
IntExpMoveEditActivity.startForEdit(itemView.context, bean)
|
||||||
|
}
|
||||||
|
|
||||||
binding.executePendingBindings()
|
binding.executePendingBindings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import android.view.View
|
|||||||
import com.lukouguoji.gjc.databinding.ItemIntExpTallySubBinding
|
import com.lukouguoji.gjc.databinding.ItemIntExpTallySubBinding
|
||||||
import com.lukouguoji.module_base.base.BaseViewHolder
|
import com.lukouguoji.module_base.base.BaseViewHolder
|
||||||
import com.lukouguoji.module_base.bean.GjcHaWb
|
import com.lukouguoji.module_base.bean.GjcHaWb
|
||||||
|
import com.lukouguoji.module_base.common.ConstantEvent
|
||||||
|
import com.lukouguoji.module_base.impl.FlowBus
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港-出港理货 子订单(分单) ViewHolder
|
* 国际出港-出港理货 子订单(分单) ViewHolder
|
||||||
@@ -20,6 +22,8 @@ class IntExpTallySubViewHolder(view: View) :
|
|||||||
// checkbox点击切换选择状态
|
// checkbox点击切换选择状态
|
||||||
binding.ivCheckbox.setOnClickListener {
|
binding.ivCheckbox.setOnClickListener {
|
||||||
bean.checked.set(!bean.checked.get())
|
bean.checked.set(!bean.checked.get())
|
||||||
|
// 通知页面刷新分单全选状态
|
||||||
|
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).tryEmit("check_changed")
|
||||||
binding.executePendingBindings()
|
binding.executePendingBindings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.lukouguoji.module_base.common.ConstantEvent
|
|||||||
import com.lukouguoji.module_base.impl.FlowBus
|
import com.lukouguoji.module_base.impl.FlowBus
|
||||||
import com.lukouguoji.module_base.bean.GjcMaWb
|
import com.lukouguoji.module_base.bean.GjcMaWb
|
||||||
import com.lukouguoji.module_base.ktx.refresh
|
import com.lukouguoji.module_base.ktx.refresh
|
||||||
|
import com.lukouguoji.module_base.ktx.ITEM_CLICK_TYPE_CUSTOMS_RESPONSE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港-出港理货 ViewHolder
|
* 国际出港-出港理货 ViewHolder
|
||||||
@@ -22,6 +23,12 @@ class IntExpTallyViewHolder(view: View) :
|
|||||||
binding.position = position
|
binding.position = position
|
||||||
binding.executePendingBindings()
|
binding.executePendingBindings()
|
||||||
|
|
||||||
|
// 侧滑菜单 - 回执按钮(关闭侧滑后分发到 ViewModel)
|
||||||
|
binding.btnResponse.setOnClickListener {
|
||||||
|
binding.swipeMenu.quickClose()
|
||||||
|
clickListener?.onItemClick(position, ITEM_CLICK_TYPE_CUSTOMS_RESPONSE)
|
||||||
|
}
|
||||||
|
|
||||||
// 图标点击切换选择状态(主单和分单独立,互不干扰)
|
// 图标点击切换选择状态(主单和分单独立,互不干扰)
|
||||||
binding.ivIcon.setOnClickListener {
|
binding.ivIcon.setOnClickListener {
|
||||||
bean.checked.set(!bean.checked.get())
|
bean.checked.set(!bean.checked.get())
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.lukouguoji.gjc.page.assemble
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始组装页「运单列表」列宽权重,表头与 item_assemble_waybill 共用以保证列对齐
|
||||||
|
*
|
||||||
|
* 首次装货成功前后,件数/重量列表头文字长短差异很大(「件数」↔「剩余件数/总件数」),
|
||||||
|
* 一套列宽无法让两种状态下 5 列文字间距都均匀,故按状态各取一套。
|
||||||
|
* 数值即平板(AutoSize 1152dp 基准)上各列的 dp 宽度;「选择」列固定 64dp 不在其中。
|
||||||
|
*/
|
||||||
|
object AssembleWaybillColumns {
|
||||||
|
|
||||||
|
const val WB_NO = 0
|
||||||
|
const val PIECES = 1
|
||||||
|
const val GAP = 2 // 件数与重量之间的间隔
|
||||||
|
const val WEIGHT = 3
|
||||||
|
const val FLIGHT = 4
|
||||||
|
|
||||||
|
/** 表头「件数 / 重量」 */
|
||||||
|
private val TOTAL = floatArrayOf(237f, 72f, 76f, 72f, 250f)
|
||||||
|
|
||||||
|
/** 表头「剩余件数/总件数 / 剩余重量/总重量」;重量列放宽到 140,容纳"120856.0/123456.0"不换行 */
|
||||||
|
private val REMAINING = floatArrayOf(199f, 120f, 57f, 140f, 192f)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun weight(column: Int, showRemaining: Boolean): Float =
|
||||||
|
(if (showRemaining) REMAINING else TOTAL)[column]
|
||||||
|
}
|
||||||
@@ -4,6 +4,11 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import androidx.appcompat.widget.AppCompatSpinner
|
||||||
|
import androidx.appcompat.widget.ListPopupWindow
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.alibaba.android.arouter.facade.annotation.Route
|
import com.alibaba.android.arouter.facade.annotation.Route
|
||||||
import com.lukouguoji.gjc.R
|
import com.lukouguoji.gjc.R
|
||||||
@@ -18,6 +23,7 @@ import com.lukouguoji.module_base.impl.observe
|
|||||||
import com.lukouguoji.module_base.interfaces.IOnItemClickListener
|
import com.lukouguoji.module_base.interfaces.IOnItemClickListener
|
||||||
import com.lukouguoji.module_base.ktx.addOnItemClickListener
|
import com.lukouguoji.module_base.ktx.addOnItemClickListener
|
||||||
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
|
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
|
||||||
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
import com.lukouguoji.module_base.router.ARouterConstants
|
import com.lukouguoji.module_base.router.ARouterConstants
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,6 +34,7 @@ class IntExpAssembleStartActivity :
|
|||||||
BaseBindingActivity<ActivityIntExpAssembleStartBinding, IntExpAssembleStartViewModel>(),
|
BaseBindingActivity<ActivityIntExpAssembleStartBinding, IntExpAssembleStartViewModel>(),
|
||||||
IOnItemClickListener {
|
IOnItemClickListener {
|
||||||
|
|
||||||
|
private var boardTypePopup: ListPopupWindow? = null
|
||||||
private var assembleInfoAdapter: CommonAdapter? = null
|
private var assembleInfoAdapter: CommonAdapter? = null
|
||||||
private var assemblePositionAdapter: CommonAdapter? = null
|
private var assemblePositionAdapter: CommonAdapter? = null
|
||||||
private var waybillAdapter: CommonAdapter? = null
|
private var waybillAdapter: CommonAdapter? = null
|
||||||
@@ -66,8 +73,18 @@ class IntExpAssembleStartActivity :
|
|||||||
setBackArrow("国际出港开始组装")
|
setBackArrow("国际出港开始组装")
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
// 为ULD编号添加大写字母和数字的输入限制
|
// 为ULD编号、IMP代码添加大写字母和数字的输入限制
|
||||||
binding.uldNoInput.et.setUpperCaseAlphanumericFilter()
|
binding.uldNoInput.et.setUpperCaseAlphanumericFilter()
|
||||||
|
binding.impCodeInput.et.setUpperCaseAlphanumericFilter()
|
||||||
|
|
||||||
|
// 板型:手动输入 + 下拉选择组合框。箭头复用 Spinner 同款背景绘制(同资源、同尺寸、同边距),
|
||||||
|
// 图标位不放图片,仅作为覆盖在箭头上的透明点击区域
|
||||||
|
binding.boardTypeInput.apply {
|
||||||
|
ll.setBackgroundResource(com.lukouguoji.module_base.R.drawable.bg_data_layout_new_dropdown)
|
||||||
|
iv.setImageDrawable(null)
|
||||||
|
iv.visibility = View.VISIBLE
|
||||||
|
iv.setOnClickListener { showBoardTypeDropdown() }
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化列表
|
// 初始化列表
|
||||||
initRecyclerViews()
|
initRecyclerViews()
|
||||||
@@ -81,6 +98,9 @@ class IntExpAssembleStartActivity :
|
|||||||
// 加载组装人列表
|
// 加载组装人列表
|
||||||
viewModel.loadAssemblerList()
|
viewModel.loadAssemblerList()
|
||||||
|
|
||||||
|
// 加载板型选项列表
|
||||||
|
viewModel.loadBoardTypeList()
|
||||||
|
|
||||||
// 监听登出事件,清空缓存
|
// 监听登出事件,清空缓存
|
||||||
FlowBus.with<Any>(ConstantEvent.LOGOUT).observe(this) {
|
FlowBus.with<Any>(ConstantEvent.LOGOUT).observe(this) {
|
||||||
viewModel.clearCachedOperator()
|
viewModel.clearCachedOperator()
|
||||||
@@ -93,6 +113,51 @@ class IntExpAssembleStartActivity :
|
|||||||
handleEditModeIntent()
|
handleEditModeIntent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 板型下拉:锚定在板型输入框正下方展开(外观与相邻 BUP 的 Spinner 下拉一致),选中后回填,仍可手动输入
|
||||||
|
*/
|
||||||
|
private fun showBoardTypeDropdown() {
|
||||||
|
val list = viewModel.boardTypeList.value ?: emptyList()
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
showToast("暂无板型选项,请手动输入")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val anchor = binding.boardTypeInput.ll
|
||||||
|
|
||||||
|
// 先收起软键盘,避免下拉被输入法遮挡
|
||||||
|
(getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)
|
||||||
|
?.hideSoftInputFromWindow(anchor.windowToken, 0)
|
||||||
|
boardTypePopup?.dismiss()
|
||||||
|
boardTypePopup = ListPopupWindow(this).apply {
|
||||||
|
anchorView = anchor
|
||||||
|
width = anchor.width
|
||||||
|
isModal = true
|
||||||
|
// 复用 BUP Spinner 的弹窗背景,保证两者下拉外观完全一致
|
||||||
|
(binding.bupFlagInput.spinner as? AppCompatSpinner)?.popupBackground
|
||||||
|
?.constantState?.newDrawable()?.mutate()
|
||||||
|
?.let { setBackgroundDrawable(it) }
|
||||||
|
setAdapter(
|
||||||
|
ArrayAdapter(
|
||||||
|
this@IntExpAssembleStartActivity,
|
||||||
|
com.lukouguoji.module_base.R.layout.item_spinner_list,
|
||||||
|
list.map { it.key }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setOnItemClickListener { _, _, position, _ ->
|
||||||
|
viewModel.onBoardTypeSelected(position)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
boardTypePopup?.dismiss()
|
||||||
|
boardTypePopup = null
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理修改模式的 Intent 参数
|
* 处理修改模式的 Intent 参数
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ class IntExpMoveActivity : BaseBindingActivity<ActivityIntExpMoveBinding, IntExp
|
|||||||
*/
|
*/
|
||||||
fun resetFilter() {
|
fun resetFilter() {
|
||||||
viewModel.by1.value = ""
|
viewModel.by1.value = ""
|
||||||
|
viewModel.dest1.value = ""
|
||||||
viewModel.awbType.value = ""
|
viewModel.awbType.value = ""
|
||||||
viewModel.spCode.value = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
package com.lukouguoji.gjc.page.move
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.lukouguoji.gjc.R
|
|
||||||
import com.lukouguoji.gjc.databinding.ActivityIntExpMoveDetailBinding
|
|
||||||
import com.lukouguoji.gjc.viewModel.IntExpMoveDetailViewModel
|
|
||||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
|
||||||
import com.lukouguoji.module_base.bean.GjcMove
|
|
||||||
import com.lukouguoji.module_base.common.Constant
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 国际出港移库「运单详情」(手机版专属页面)
|
|
||||||
*
|
|
||||||
* 由手机版列表卡片的「查看详情」进入;平板端列表布局不含该入口,故不会走到本页。
|
|
||||||
*/
|
|
||||||
class IntExpMoveDetailActivity :
|
|
||||||
BaseBindingActivity<ActivityIntExpMoveDetailBinding, IntExpMoveDetailViewModel>() {
|
|
||||||
|
|
||||||
override fun layoutId() = R.layout.activity_int_exp_move_detail
|
|
||||||
override fun viewModelClass() = IntExpMoveDetailViewModel::class.java
|
|
||||||
|
|
||||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
|
||||||
setBackArrow("运单详情")
|
|
||||||
binding.viewModel = viewModel
|
|
||||||
viewModel.initOnCreated(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun start(context: Context, bean: GjcMove) {
|
|
||||||
context.startActivity(
|
|
||||||
Intent(context, IntExpMoveDetailActivity::class.java)
|
|
||||||
.putExtra(Constant.Key.DATA, Gson().toJson(bean))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.lukouguoji.gjc.page.move
|
||||||
|
|
||||||
|
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.ActivityIntExpMoveEditBinding
|
||||||
|
import com.lukouguoji.gjc.viewModel.IntExpMoveEditViewModel
|
||||||
|
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||||
|
import com.lukouguoji.module_base.bean.GjcMove
|
||||||
|
import com.lukouguoji.module_base.common.Constant
|
||||||
|
import com.lukouguoji.module_base.common.DetailsPageType
|
||||||
|
import com.lukouguoji.module_base.ktx.addOnItemClickListener
|
||||||
|
import com.lukouguoji.module_base.ktx.commonAdapter
|
||||||
|
import com.lukouguoji.module_base.router.ARouterConstants
|
||||||
|
import com.lukouguoji.module_base.util.DeviceUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际出港移库编辑/详情页
|
||||||
|
* 编辑模式仅支持拍照上传与备注修改
|
||||||
|
* 布局双变体:layout-sw600dp/(平板)与 layout/(手机,设计稿「运单详情」样式),共用本 Activity 与 ViewModel
|
||||||
|
*/
|
||||||
|
@Route(path = ARouterConstants.ACTIVITY_URL_INT_EXP_MOVE_EDIT)
|
||||||
|
class IntExpMoveEditActivity :
|
||||||
|
BaseBindingActivity<ActivityIntExpMoveEditBinding, IntExpMoveEditViewModel>() {
|
||||||
|
|
||||||
|
override fun layoutId() = R.layout.activity_int_exp_move_edit
|
||||||
|
|
||||||
|
override fun viewModelClass() = IntExpMoveEditViewModel::class.java
|
||||||
|
|
||||||
|
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||||
|
viewModel.initOnCreated(intent)
|
||||||
|
|
||||||
|
val isPhone = DeviceUtil.isPhone()
|
||||||
|
when (viewModel.pageType.value) {
|
||||||
|
DetailsPageType.Modify -> setBackArrow(if (isPhone) "移库编辑" else "国际出港移库编辑")
|
||||||
|
else -> setBackArrow(if (isPhone) "运单详情" else "国际出港移库详情")
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
|
// 绑定图片 RecyclerView 引用
|
||||||
|
viewModel.rvImages = binding.rvImages
|
||||||
|
|
||||||
|
// 绑定图片列表点击事件(编辑模式下可删除图片)
|
||||||
|
binding.rvImages.addOnItemClickListener(viewModel)
|
||||||
|
|
||||||
|
// 监听图片列表变化并更新 adapter
|
||||||
|
viewModel.imageList.observe(this) { images ->
|
||||||
|
binding.rvImages.commonAdapter()?.refresh(images)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* 列表 Bean 一并传入:detail 接口的承运人、出运路径等字段可能为空,
|
||||||
|
* 由 ViewModel 用列表数据回填
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun startForEdit(context: Context, bean: GjcMove) {
|
||||||
|
start(context, bean, DetailsPageType.Modify)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun startForDetails(context: Context, bean: GjcMove) {
|
||||||
|
start(context, bean, DetailsPageType.Details)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun start(context: Context, bean: GjcMove, pageType: DetailsPageType) {
|
||||||
|
val starter = Intent(context, IntExpMoveEditActivity::class.java)
|
||||||
|
.putExtra(Constant.Key.PAGE_TYPE, pageType.name)
|
||||||
|
.putExtra(Constant.Key.ID, bean.maWbId.toString())
|
||||||
|
.putExtra(Constant.Key.DATA, Gson().toJson(bean))
|
||||||
|
context.startActivity(starter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ class GjcAssembleWeightEditViewModel : BaseViewModel() {
|
|||||||
val totalPc = MutableLiveData("") // 总件数
|
val totalPc = MutableLiveData("") // 总件数
|
||||||
val totalWeight = MutableLiveData("") // 总重量
|
val totalWeight = MutableLiveData("") // 总重量
|
||||||
val cargoWeight = MutableLiveData("") // 货重
|
val cargoWeight = MutableLiveData("") // 货重
|
||||||
|
val consumeWeight = MutableLiveData("") // 耗材重量
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// 底部统计字段
|
// 底部统计字段
|
||||||
@@ -74,6 +75,7 @@ class GjcAssembleWeightEditViewModel : BaseViewModel() {
|
|||||||
totalPc.value = it.pc.toString()
|
totalPc.value = it.pc.toString()
|
||||||
totalWeight.value = it.totalWeight.toString()
|
totalWeight.value = it.totalWeight.toString()
|
||||||
cargoWeight.value = it.cargoWeight.toString()
|
cargoWeight.value = it.cargoWeight.toString()
|
||||||
|
consumeWeight.value = it.consumeWeight.toString()
|
||||||
|
|
||||||
// 保存参数用于API调用
|
// 保存参数用于API调用
|
||||||
useId = it.useId
|
useId = it.useId
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.lukouguoji.module_base.ktx.noNull
|
|||||||
import com.lukouguoji.module_base.ktx.showToast
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
import com.lukouguoji.module_base.ktx.toRequestBody
|
import com.lukouguoji.module_base.ktx.toRequestBody
|
||||||
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
|
import com.lukouguoji.module_base.ktx.verifyNullOrEmpty
|
||||||
|
import com.lukouguoji.module_base.db.perference.SharedPreferenceUtil
|
||||||
import com.lukouguoji.module_base.model.BluetoothDialogModel
|
import com.lukouguoji.module_base.model.BluetoothDialogModel
|
||||||
import com.lukouguoji.module_base.model.ScanModel
|
import com.lukouguoji.module_base.model.ScanModel
|
||||||
import com.lukouguoji.module_base.util.Common
|
import com.lukouguoji.module_base.util.Common
|
||||||
@@ -34,6 +35,11 @@ import java.util.Date
|
|||||||
*/
|
*/
|
||||||
class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// 记录上一次称重完成时使用的通道号,后续称重默认选中
|
||||||
|
private const val KEY_LAST_PASSAGEWAY_ID = "gjc_box_weighing_last_passageway_id"
|
||||||
|
}
|
||||||
|
|
||||||
// 数据Bean
|
// 数据Bean
|
||||||
val dataBean = MutableLiveData(GjcUldUseBean())
|
val dataBean = MutableLiveData(GjcUldUseBean())
|
||||||
|
|
||||||
@@ -51,6 +57,7 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
|||||||
val totalWeight = MutableLiveData("0") // 总重(红色)
|
val totalWeight = MutableLiveData("0") // 总重(红色)
|
||||||
val netWeight = MutableLiveData("0") // 装机重(蓝色)
|
val netWeight = MutableLiveData("0") // 装机重(蓝色)
|
||||||
val cargoWeight = MutableLiveData("0") // 货重(绿色)
|
val cargoWeight = MutableLiveData("0") // 货重(绿色)
|
||||||
|
val consumeWeight = MutableLiveData("0") // 耗材重量(只读,随 ULD 记录带出)
|
||||||
|
|
||||||
// 需要自动查询的独立字段(用于监听输入变化)
|
// 需要自动查询的独立字段(用于监听输入变化)
|
||||||
val carId = MutableLiveData("") // 架子车号
|
val carId = MutableLiveData("") // 架子车号
|
||||||
@@ -113,9 +120,11 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
|||||||
val editBean = intent?.getSerializableExtra(Constant.Key.BEAN) as? GjcUldUseBean
|
val editBean = intent?.getSerializableExtra(Constant.Key.BEAN) as? GjcUldUseBean
|
||||||
if (editBean != null) {
|
if (editBean != null) {
|
||||||
prefillFromBean(editBean)
|
prefillFromBean(editBean)
|
||||||
loadPassagewayList(editBean.passagewayId)
|
// 优先本条记录自带的通道号,没有则回退到上一次称重记住的通道号(App 级持久化)
|
||||||
|
loadPassagewayList(editBean.passagewayId.ifEmpty { null })
|
||||||
loadPlCloseList(editBean.plClose)
|
loadPlCloseList(editBean.plClose)
|
||||||
} else {
|
} else {
|
||||||
|
// 新增模式:默认选中上一次称重使用的通道号
|
||||||
loadPassagewayList(null)
|
loadPassagewayList(null)
|
||||||
loadPlCloseList(null)
|
loadPlCloseList(null)
|
||||||
}
|
}
|
||||||
@@ -138,6 +147,7 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
|||||||
totalWeight.value = bean.totalWeight.toString()
|
totalWeight.value = bean.totalWeight.toString()
|
||||||
netWeight.value = bean.netWeight.toString()
|
netWeight.value = bean.netWeight.toString()
|
||||||
cargoWeight.value = bean.cargoWeight.toString()
|
cargoWeight.value = bean.cargoWeight.toString()
|
||||||
|
consumeWeight.value = bean.consumeWeight.toString()
|
||||||
diBangWeight.value = bean.totalWeight.toString()
|
diBangWeight.value = bean.totalWeight.toString()
|
||||||
|
|
||||||
// 通道号 Spinner 绑定到 channel(KeyValue.value 即 passagewayId)
|
// 通道号 Spinner 绑定到 channel(KeyValue.value 即 passagewayId)
|
||||||
@@ -229,15 +239,27 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载通道号列表
|
* 加载通道号列表
|
||||||
* @param checkedValue 编辑模式下需要选中的 passagewayId;为空则按返回顺序展示
|
* @param preferredId 编辑模式下本条记录自带的 passagewayId;为空则回退到上一次称重记住的通道号
|
||||||
|
*
|
||||||
|
* 注意:Spinner 带 hint 时默认停在 hint 项,并不会显示列表首项,
|
||||||
|
* 所以必须显式给 channel 赋值才能选中,只调列表顺序无效。
|
||||||
*/
|
*/
|
||||||
private fun loadPassagewayList(checkedValue: String?) {
|
private fun loadPassagewayList(preferredId: String?) {
|
||||||
launchCollect({
|
launchCollect({
|
||||||
NetApply.api.getDictList("GJPASSAGEWAY")
|
NetApply.api.getDictList("GJPASSAGEWAY")
|
||||||
}) {
|
}) {
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
val list = (it.data ?: emptyList()).map { b -> b.toKeyValue() }
|
val list = (it.data ?: emptyList()).map { b -> b.toKeyValue() }
|
||||||
passagewayList.value = reorderChecked(list, checkedValue)
|
passagewayList.value = list
|
||||||
|
|
||||||
|
// 选中优先级:本条记录的通道号 → 上一次称重记住的通道号(SharedPreference,跨页面、跨启动保留)
|
||||||
|
val lastPassagewayId = SharedPreferenceUtil.getString(KEY_LAST_PASSAGEWAY_ID)
|
||||||
|
val selected = listOf(preferredId, lastPassagewayId).firstOrNull { id ->
|
||||||
|
!id.isNullOrEmpty() && list.any { kv -> kv.value == id }
|
||||||
|
}
|
||||||
|
if (selected != null) {
|
||||||
|
channel.value = selected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,15 +295,16 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
|||||||
/**
|
/**
|
||||||
* 计算重量字段
|
* 计算重量字段
|
||||||
* 装机重 = 总重 - 架子车自重
|
* 装机重 = 总重 - 架子车自重
|
||||||
* 货重 = 装机重 - ULD自重
|
* 货重 = 装机重 - ULD自重 - 耗材自重
|
||||||
*/
|
*/
|
||||||
private fun calculateWeights() {
|
private fun calculateWeights() {
|
||||||
val total = totalWeight.value?.toDoubleOrNull() ?: 0.0
|
val total = totalWeight.value?.toDoubleOrNull() ?: 0.0
|
||||||
val carW = dataBean.value?.carWeight ?: 0.0
|
val carW = dataBean.value?.carWeight ?: 0.0
|
||||||
val uldW = dataBean.value?.uldWeight ?: 0.0
|
val uldW = dataBean.value?.uldWeight ?: 0.0
|
||||||
|
val consumeW = consumeWeight.value?.toDoubleOrNull() ?: 0.0
|
||||||
|
|
||||||
val net = total - carW
|
val net = total - carW
|
||||||
val cargo = net - uldW
|
val cargo = net - uldW - consumeW
|
||||||
|
|
||||||
netWeight.value = if (net > 0) net.toString() else "0"
|
netWeight.value = if (net > 0) net.toString() else "0"
|
||||||
cargoWeight.value = if (cargo > 0) cargo.toString() else "0"
|
cargoWeight.value = if (cargo > 0) cargo.toString() else "0"
|
||||||
@@ -334,7 +357,8 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
|||||||
totalWeight.value = "0"
|
totalWeight.value = "0"
|
||||||
netWeight.value = "0"
|
netWeight.value = "0"
|
||||||
cargoWeight.value = "0"
|
cargoWeight.value = "0"
|
||||||
channel.value = ""
|
consumeWeight.value = "0"
|
||||||
|
// 通道号不清空:保留当前选择,与 Spinner 显示保持一致,后续称重沿用上一次的通道号
|
||||||
printTag.value = false
|
printTag.value = false
|
||||||
|
|
||||||
// 重置独立字段
|
// 重置独立字段
|
||||||
@@ -437,6 +461,11 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
|||||||
if (result.verifySuccess()) {
|
if (result.verifySuccess()) {
|
||||||
showToast("完成复磅")
|
showToast("完成复磅")
|
||||||
|
|
||||||
|
// 记住本次使用的通道号,后续称重默认选中
|
||||||
|
if (bean.passagewayId.isNotEmpty()) {
|
||||||
|
SharedPreferenceUtil.addString(KEY_LAST_PASSAGEWAY_ID, bean.passagewayId)
|
||||||
|
}
|
||||||
|
|
||||||
// 如果勾选了打印挂签,则执行打印
|
// 如果勾选了打印挂签,则执行打印
|
||||||
if (printTag.value == true) {
|
if (printTag.value == true) {
|
||||||
executePrint()
|
executePrint()
|
||||||
@@ -519,6 +548,9 @@ class GjcBoxWeighingAddViewModel : BaseViewModel() {
|
|||||||
if (usingData != null && usingData.useId > 0) {
|
if (usingData != null && usingData.useId > 0) {
|
||||||
// 保存返回的数据,用于提交时传递
|
// 保存返回的数据,用于提交时传递
|
||||||
usingUldData = usingData
|
usingUldData = usingData
|
||||||
|
// 耗材重量随该记录带回,需要重新参与货重计算
|
||||||
|
consumeWeight.value = usingData.consumeWeight.toString()
|
||||||
|
calculateWeights()
|
||||||
} else {
|
} else {
|
||||||
// 没有 useId,说明未组装,不可复磅
|
// 没有 useId,说明未组装,不可复磅
|
||||||
usingUldData = null
|
usingUldData = null
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.lukouguoji.gjc.viewModel
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.lukouguoji.gjc.R
|
import com.lukouguoji.gjc.R
|
||||||
import com.lukouguoji.gjc.holder.GjcBoxWeighingViewHolder
|
import com.lukouguoji.gjc.holder.GjcBoxWeighingViewHolder
|
||||||
import com.lukouguoji.module_base.base.BasePageViewModel
|
import com.lukouguoji.module_base.base.BasePageViewModel
|
||||||
@@ -20,6 +21,7 @@ import com.lukouguoji.module_base.util.PrinterUtils
|
|||||||
import dev.utils.app.info.KeyValue
|
import dev.utils.app.info.KeyValue
|
||||||
import dev.utils.common.DateUtils
|
import dev.utils.common.DateUtils
|
||||||
import com.lukouguoji.module_base.ktx.formatDate
|
import com.lukouguoji.module_base.ktx.formatDate
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际出港板箱过磅 ViewModel
|
* 国际出港板箱过磅 ViewModel
|
||||||
@@ -96,16 +98,19 @@ class GjcBoxWeighingViewModel : BasePageViewModel() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验多选
|
if (selectedItems.size == 1) {
|
||||||
if (selectedItems.size > 1) {
|
// 单条:保持原有打印路径
|
||||||
showToast("只能选择一条记录进行打印")
|
val bean = selectedItems.first()
|
||||||
return
|
BluetoothDialogModel().showCallBack {
|
||||||
}
|
PrinterUtils.printGjcBoxWeighing(bean)
|
||||||
|
}
|
||||||
// 执行打印
|
} else {
|
||||||
val bean = selectedItems.first()
|
// 多条:逐张串行批量打印
|
||||||
BluetoothDialogModel().showCallBack {
|
BluetoothDialogModel().showCallBack {
|
||||||
PrinterUtils.printGjcBoxWeighing(bean)
|
viewModelScope.launch {
|
||||||
|
PrinterUtils.printGjcBoxWeighingBatch(selectedItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,33 +25,34 @@ import com.lukouguoji.module_base.ktx.formatDate
|
|||||||
class GjcQueryViewModel : BasePageViewModel() {
|
class GjcQueryViewModel : BasePageViewModel() {
|
||||||
|
|
||||||
// ==================== 搜索条件 ====================
|
// ==================== 搜索条件 ====================
|
||||||
|
val dateState = MutableLiveData("0") // 日期类型(0:入库,1:离港)
|
||||||
val flightDateStart = MutableLiveData<String>(DateUtils.getCurrentTime().formatDate()) // 航班日期起
|
val flightDateStart = MutableLiveData<String>(DateUtils.getCurrentTime().formatDate()) // 航班日期起
|
||||||
val flightDateEnd = MutableLiveData("") // 航班日期止
|
val flightDateEnd = MutableLiveData("") // 航班日期止
|
||||||
val agentId = MutableLiveData("") // 代理ID
|
val agentId = MutableLiveData("") // 代理ID
|
||||||
val outStatus = MutableLiveData("") // 出库状态
|
val flightNo = MutableLiveData("") // 航班号
|
||||||
val waybillNo = MutableLiveData("") // 运单号
|
val waybillNo = MutableLiveData("") // 运单号
|
||||||
|
|
||||||
// ==================== 下拉列表 ====================
|
// ==================== 下拉列表 ====================
|
||||||
// 代理下拉列表(从API获取)
|
// 日期类型下拉列表(对应接口 dateState)
|
||||||
val agentList = MutableLiveData(listOf(KeyValue("全部", "")))
|
val dateStateList = MutableLiveData(
|
||||||
|
|
||||||
// 出库状态下拉列表
|
|
||||||
val outStatusList = MutableLiveData(
|
|
||||||
listOf(
|
listOf(
|
||||||
KeyValue("全部", ""),
|
KeyValue("入库", "0"),
|
||||||
KeyValue("未出库", "0"),
|
KeyValue("离港", "1")
|
||||||
KeyValue("已出库", "1")
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 代理下拉列表(从API获取)
|
||||||
|
val agentList = MutableLiveData(listOf(KeyValue("全部", "")))
|
||||||
|
|
||||||
// ==================== 适配器配置 ====================
|
// ==================== 适配器配置 ====================
|
||||||
val itemViewHolder = GjcQueryViewHolder::class.java
|
val itemViewHolder = GjcQueryViewHolder::class.java
|
||||||
val itemLayoutId = R.layout.item_gjc_query
|
val itemLayoutId = R.layout.item_gjc_query
|
||||||
|
|
||||||
// ==================== 统计数据 ====================
|
// ==================== 统计数据 ====================
|
||||||
val totalCount = MutableLiveData("0") // 合计票数
|
val totalCount = MutableLiveData("0") // 合计票数
|
||||||
val totalPc = MutableLiveData("0") // 总件数
|
val totalPc = MutableLiveData("0") // 总件数
|
||||||
val totalWeight = MutableLiveData("0") // 总重量
|
val totalWeight = MutableLiveData("0") // 总重量
|
||||||
|
val totalFlightNumber = MutableLiveData("0") // 航班航次
|
||||||
|
|
||||||
// ==================== 筛选面板 ====================
|
// ==================== 筛选面板 ====================
|
||||||
val filterVisible = MutableLiveData(false)
|
val filterVisible = MutableLiveData(false)
|
||||||
@@ -59,6 +60,9 @@ class GjcQueryViewModel : BasePageViewModel() {
|
|||||||
// 运单类型列表(字典接口 typeCode/awb,type=IO 国际出港)
|
// 运单类型列表(字典接口 typeCode/awb,type=IO 国际出港)
|
||||||
val awbTypeList = MutableLiveData<List<KeyValue>>(emptyList())
|
val awbTypeList = MutableLiveData<List<KeyValue>>(emptyList())
|
||||||
|
|
||||||
|
// 特码列表(与国际出港待记重页面同源:flag=1 国际,ieFlag 为空)
|
||||||
|
val spCodeList = MutableLiveData<List<KeyValue>>(emptyList())
|
||||||
|
|
||||||
// 业务类型列表
|
// 业务类型列表
|
||||||
val businessTypeList = MutableLiveData(
|
val businessTypeList = MutableLiveData(
|
||||||
listOf(
|
listOf(
|
||||||
@@ -70,48 +74,24 @@ class GjcQueryViewModel : BasePageViewModel() {
|
|||||||
|
|
||||||
// ==================== 筛选条件 ====================
|
// ==================== 筛选条件 ====================
|
||||||
val spCode = MutableLiveData("") // 特码
|
val spCode = MutableLiveData("") // 特码
|
||||||
val flightNo = MutableLiveData("") // 航班号
|
|
||||||
val dest = MutableLiveData("") // 目的港
|
val dest = MutableLiveData("") // 目的港
|
||||||
val awbType = MutableLiveData("") // 运单类型
|
val awbType = MutableLiveData("") // 运单类型
|
||||||
val businessType = MutableLiveData("") // 业务类型
|
val businessType = MutableLiveData("") // 业务类型
|
||||||
val goodsCn = MutableLiveData("") // 品名(中)
|
val goodsCn = MutableLiveData("") // 品名(中)
|
||||||
|
|
||||||
// ==================== 5.5寸手机版专属 ====================
|
// ==================== 5.5寸手机版三格统计 ====================
|
||||||
// 特码下拉列表(设计稿筛选弹层为下拉;平板筛选为文本输入,不加载)
|
// 总票数 / 已入库 / 已离港,均取 pageQueryTotal 出参(wbNumber / storedNumber / departedNumber),
|
||||||
val specialCodeList = MutableLiveData<List<KeyValue>>(emptyList())
|
// 与列表同条件、服务端全量口径(平板布局不绑定)
|
||||||
|
|
||||||
// 三格统计:总票数 / 已入库 / 已离港,均取 pageQueryTotal 出参(wbNumber / storedNumber / departedNumber),
|
|
||||||
// 与列表同条件、服务端全量口径
|
|
||||||
val storedCount = MutableLiveData("0")
|
val storedCount = MutableLiveData("0")
|
||||||
val departedCount = MutableLiveData("0")
|
val departedCount = MutableLiveData("0")
|
||||||
|
|
||||||
// 日期类型(0:入库,1:离港),与主干平板端一致。后端 beginDate/endDate 仅在携带 dateState 时生效
|
|
||||||
// (内网实测:不带 dateState 时起止日期被忽略)。平板布局(本分支)不绑定,phoneExtras 关闭时不进请求
|
|
||||||
val dateState = MutableLiveData("0")
|
|
||||||
val dateStateList = MutableLiveData(listOf(KeyValue("入库", "0"), KeyValue("离港", "1")))
|
|
||||||
|
|
||||||
private var phoneExtrasEnabled = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手机形态专属初始化(Activity 在 DeviceUtil.isPhone() 时调用)。
|
|
||||||
* 平板端不调用,请求次数与行为零变化。
|
|
||||||
*/
|
|
||||||
fun initPhoneExtras() {
|
|
||||||
phoneExtrasEnabled = true
|
|
||||||
// flag=1 国际;ieFlag 必须留空——传 "E" 后端返回空数组(与既有国际出港页面口径一致)
|
|
||||||
DictUtils.getSpecialCodeList(addAll = false, flag = 1, ieFlag = "") { list ->
|
|
||||||
specialCodeList.postValue(listOf(KeyValue("全部", "")) + list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否有筛选条件(任意一个非空则为 true,用于筛选按钮红点)
|
// 是否有筛选条件(任意一个非空则为 true,用于筛选按钮红点)
|
||||||
val hasFilter: MediatorLiveData<Boolean> = MediatorLiveData<Boolean>().apply {
|
val hasFilter: MediatorLiveData<Boolean> = MediatorLiveData<Boolean>().apply {
|
||||||
val update = { _: Any? ->
|
val update = { _: Any? ->
|
||||||
value = listOf(spCode, flightNo, dest, awbType, businessType, goodsCn)
|
value = listOf(spCode, dest, awbType, businessType, goodsCn)
|
||||||
.any { !it.value.isNullOrEmpty() }
|
.any { !it.value.isNullOrEmpty() }
|
||||||
}
|
}
|
||||||
addSource(spCode, update)
|
addSource(spCode, update)
|
||||||
addSource(flightNo, update)
|
|
||||||
addSource(dest, update)
|
addSource(dest, update)
|
||||||
addSource(awbType, update)
|
addSource(awbType, update)
|
||||||
addSource(businessType, update)
|
addSource(businessType, update)
|
||||||
@@ -155,7 +135,6 @@ class GjcQueryViewModel : BasePageViewModel() {
|
|||||||
*/
|
*/
|
||||||
fun resetFilter() {
|
fun resetFilter() {
|
||||||
spCode.value = ""
|
spCode.value = ""
|
||||||
flightNo.value = ""
|
|
||||||
dest.value = ""
|
dest.value = ""
|
||||||
awbType.value = ""
|
awbType.value = ""
|
||||||
businessType.value = ""
|
businessType.value = ""
|
||||||
@@ -171,7 +150,7 @@ class GjcQueryViewModel : BasePageViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化筛选下拉列表(运单类型从字典接口获取,type=IO 国际出港)
|
* 初始化筛选下拉列表(运单类型、特码均从字典接口获取)
|
||||||
*/
|
*/
|
||||||
fun initFilterLists() {
|
fun initFilterLists() {
|
||||||
DictUtils.getWaybillTypeList(
|
DictUtils.getWaybillTypeList(
|
||||||
@@ -181,6 +160,15 @@ class GjcQueryViewModel : BasePageViewModel() {
|
|||||||
) {
|
) {
|
||||||
awbTypeList.postValue(it)
|
awbTypeList.postValue(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 特码列表(参数与国际出港待记重页面一致)
|
||||||
|
DictUtils.getSpecialCodeList(
|
||||||
|
flag = 1, // 国际
|
||||||
|
ieFlag = "", // 空字符串
|
||||||
|
parentcode = "" // 无父级
|
||||||
|
) {
|
||||||
|
spCodeList.postValue(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -188,45 +176,31 @@ class GjcQueryViewModel : BasePageViewModel() {
|
|||||||
* 核心方法:调用API获取数据并转换PageInfo为BaseListBean
|
* 核心方法:调用API获取数据并转换PageInfo为BaseListBean
|
||||||
*/
|
*/
|
||||||
override fun getData() {
|
override fun getData() {
|
||||||
// 手机版:日期类型,仅在有起止日期时携带(平板 phoneExtrasEnabled 恒 false → null 不进请求)
|
// 公共查询条件(列表与统计接口共用)
|
||||||
val hasDate = !flightDateStart.value.isNullOrEmpty() || !flightDateEnd.value.isNullOrEmpty()
|
val commonParams = mapOf(
|
||||||
val dateStateParam = if (phoneExtrasEnabled && hasDate) dateState.value?.toIntOrNull() else null
|
// 日期类型(0:入库,1:离港)+ 日期范围
|
||||||
|
"dateState" to dateState.value?.ifEmpty { null }?.toIntOrNull(),
|
||||||
|
"beginDate" to flightDateStart.value?.ifEmpty { null },
|
||||||
|
"endDate" to flightDateEnd.value?.ifEmpty { null },
|
||||||
|
"agentCode" to agentId.value?.ifEmpty { null },
|
||||||
|
"fno" to flightNo.value?.ifEmpty { null },
|
||||||
|
"wbNo" to waybillNo.value?.ifEmpty { null },
|
||||||
|
// 筛选面板条件
|
||||||
|
"spCode" to spCode.value?.ifEmpty { null },
|
||||||
|
"dest" to dest.value?.ifEmpty { null },
|
||||||
|
"awbType" to awbType.value?.ifEmpty { null },
|
||||||
|
"businessType" to businessType.value?.ifEmpty { null },
|
||||||
|
"goodsCn" to goodsCn.value?.ifEmpty { null }
|
||||||
|
)
|
||||||
|
|
||||||
// 构建查询参数(列表接口)
|
// 构建查询参数(列表接口)
|
||||||
val listParams = mapOf(
|
val listParams = (commonParams + mapOf(
|
||||||
"pageNum" to pageModel.page,
|
"pageNum" to pageModel.page,
|
||||||
"pageSize" to pageModel.limit,
|
"pageSize" to pageModel.limit
|
||||||
"dateState" to dateStateParam,
|
)).toRequestBody()
|
||||||
"beginDate" to flightDateStart.value!!.ifEmpty { null },
|
|
||||||
"endDate" to flightDateEnd.value!!.ifEmpty { null },
|
|
||||||
"agentCode" to agentId.value!!.ifEmpty { null },
|
|
||||||
"outState" to outStatus.value!!.ifEmpty { null },
|
|
||||||
"wbNo" to waybillNo.value!!.ifEmpty { null },
|
|
||||||
// 筛选条件(暂未使用)
|
|
||||||
"spCode" to spCode.value!!.ifEmpty { null },
|
|
||||||
"fno" to flightNo.value!!.ifEmpty { null },
|
|
||||||
"dest" to dest.value!!.ifEmpty { null },
|
|
||||||
"awbType" to awbType.value!!.ifEmpty { null },
|
|
||||||
"businessType" to businessType.value!!.ifEmpty { null },
|
|
||||||
"goodsCn" to goodsCn.value!!.ifEmpty { null }
|
|
||||||
).toRequestBody()
|
|
||||||
|
|
||||||
// 构建查询参数(统计接口 - 使用相同的搜索条件)
|
// 构建查询参数(统计接口 - 使用相同的搜索条件)
|
||||||
val totalParams = mapOf(
|
val totalParams = commonParams.toRequestBody()
|
||||||
"dateState" to dateStateParam,
|
|
||||||
"beginDate" to flightDateStart.value!!.ifEmpty { null },
|
|
||||||
"endDate" to flightDateEnd.value!!.ifEmpty { null },
|
|
||||||
"agentCode" to agentId.value!!.ifEmpty { null },
|
|
||||||
"outState" to outStatus.value!!.ifEmpty { null },
|
|
||||||
"wbNo" to waybillNo.value!!.ifEmpty { null },
|
|
||||||
// 筛选条件
|
|
||||||
"spCode" to spCode.value!!.ifEmpty { null },
|
|
||||||
"fno" to flightNo.value!!.ifEmpty { null },
|
|
||||||
"dest" to dest.value!!.ifEmpty { null },
|
|
||||||
"awbType" to awbType.value!!.ifEmpty { null },
|
|
||||||
"businessType" to businessType.value!!.ifEmpty { null },
|
|
||||||
"goodsCn" to goodsCn.value!!.ifEmpty { null }
|
|
||||||
).toRequestBody()
|
|
||||||
|
|
||||||
// 获取列表数据(显示loading)
|
// 获取列表数据(显示loading)
|
||||||
launchLoadingCollect({
|
launchLoadingCollect({
|
||||||
@@ -247,6 +221,7 @@ class GjcQueryViewModel : BasePageViewModel() {
|
|||||||
totalCount.value = (data?.wbNumber ?: 0).toString()
|
totalCount.value = (data?.wbNumber ?: 0).toString()
|
||||||
totalPc.value = (data?.totalPc ?: 0).toString()
|
totalPc.value = (data?.totalPc ?: 0).toString()
|
||||||
totalWeight.value = (data?.totalWeight ?: 0.0).toString()
|
totalWeight.value = (data?.totalWeight ?: 0.0).toString()
|
||||||
|
totalFlightNumber.value = (data?.flightNumber ?: 0).toString()
|
||||||
// 手机三格统计分项(平板布局不绑定,无影响)
|
// 手机三格统计分项(平板布局不绑定,无影响)
|
||||||
storedCount.value = (data?.storedNumber ?: 0).toString()
|
storedCount.value = (data?.storedNumber ?: 0).toString()
|
||||||
departedCount.value = (data?.departedNumber ?: 0).toString()
|
departedCount.value = (data?.departedNumber ?: 0).toString()
|
||||||
|
|||||||
@@ -46,23 +46,11 @@ class GjcWeighingRecordViewModel : BasePageViewModel() {
|
|||||||
val totalWeight = MutableLiveData("0") // 总重量
|
val totalWeight = MutableLiveData("0") // 总重量
|
||||||
val cargoWeight = MutableLiveData("0") // 总货重
|
val cargoWeight = MutableLiveData("0") // 总货重
|
||||||
|
|
||||||
// ========== 5.5寸手机版专属字段(平板不调用 initPhoneExtras,行为零变化) ==========
|
// ========== 5.5寸手机版 ==========
|
||||||
|
|
||||||
// 手机版筛选弹层与平板端查询参数保持一致(后端确认):航班日期 / 代理人 / 特码,
|
// 手机版筛选弹层与平板端查询参数保持一致(后端确认):航班日期 / 代理人 / 特码,
|
||||||
// 运单号走顶部搜索框;dest / opId / 收运时间范围后端均不支持(dest、opId 内网实测被忽略)
|
// 运单号走顶部搜索框;dest / opId / 收运时间范围后端均不支持(dest、opId 内网实测被忽略)
|
||||||
|
|
||||||
/** 由手机形态的 Activity 置 true,平板保持 false(不产生任何额外请求与参数) */
|
|
||||||
private var phoneExtrasEnabled = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 开启手机版专属数据:放开「今天」默认日期(手机默认查全量,航班日期在筛选弹层可选)。
|
|
||||||
* 仅由手机形态的 Activity 调用。
|
|
||||||
*/
|
|
||||||
fun initPhoneExtras() {
|
|
||||||
phoneExtrasEnabled = true
|
|
||||||
flightDate.value = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// 方法区
|
// 方法区
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@@ -124,17 +112,11 @@ class GjcWeighingRecordViewModel : BasePageViewModel() {
|
|||||||
when (selectedItems.size) {
|
when (selectedItems.size) {
|
||||||
0 -> {
|
0 -> {
|
||||||
// 没有选择单据:直接跳转(不带参数)
|
// 没有选择单据:直接跳转(不带参数)
|
||||||
com.alibaba.android.arouter.launcher.ARouter.getInstance()
|
openArriveDeclare(null)
|
||||||
.build(com.lukouguoji.module_base.router.ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE)
|
|
||||||
.navigation()
|
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
// 选择一个单据:跳转到出港运抵页面,携带运单号
|
// 选择一个单据:跳转到出港运抵页面,携带运单号
|
||||||
val waybillNo = selectedItems[0].wbNo
|
openArriveDeclare(selectedItems[0].wbNo)
|
||||||
com.alibaba.android.arouter.launcher.ARouter.getInstance()
|
|
||||||
.build(com.lukouguoji.module_base.router.ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE)
|
|
||||||
.withString("wbNoParam", waybillNo)
|
|
||||||
.navigation()
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// 选择多个单据:Toast 提示
|
// 选择多个单据:Toast 提示
|
||||||
@@ -143,6 +125,19 @@ class GjcWeighingRecordViewModel : BasePageViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* 跳转出港运抵(运抵申报);wbNo 非空时携带运单号自动查询。
|
||||||
|
* 平板「运抵申报」按钮与手机卡片「运抵申报」(ViewHolder 调用)共用。
|
||||||
|
*/
|
||||||
|
fun openArriveDeclare(wbNo: String?) {
|
||||||
|
val postcard = com.alibaba.android.arouter.launcher.ARouter.getInstance()
|
||||||
|
.build(com.lukouguoji.module_base.router.ARouterConstants.ACTIVITY_URL_INT_EXP_ARRIVE)
|
||||||
|
if (!wbNo.isNullOrEmpty()) postcard.withString("wbNoParam", wbNo)
|
||||||
|
postcard.navigation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取列表数据
|
* 获取列表数据
|
||||||
*/
|
*/
|
||||||
@@ -156,16 +151,6 @@ class GjcWeighingRecordViewModel : BasePageViewModel() {
|
|||||||
"wbNo" to waybillNo.value!!.ifEmpty { null },
|
"wbNo" to waybillNo.value!!.ifEmpty { null },
|
||||||
)
|
)
|
||||||
|
|
||||||
// 手机版专属条件(平板 phoneExtrasEnabled 恒为 false,请求体与改造前完全一致)
|
|
||||||
if (phoneExtrasEnabled) {
|
|
||||||
// 搜索分流(实测):11 位数字走 wbNo 精确;其余走 likeNo(后端按 8 位 no 匹配)
|
|
||||||
val key = waybillNo.value?.trim() ?: ""
|
|
||||||
if (key.isNotEmpty() && key.length != 11) {
|
|
||||||
baseParams["wbNo"] = null
|
|
||||||
baseParams["likeNo"] = key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建查询参数(列表接口)
|
// 构建查询参数(列表接口)
|
||||||
val listParams = (baseParams + mapOf(
|
val listParams = (baseParams + mapOf(
|
||||||
"pageNum" to pageModel.page,
|
"pageNum" to pageModel.page,
|
||||||
|
|||||||
@@ -66,39 +66,19 @@ class GjcWeighingStartViewModel : BaseViewModel() {
|
|||||||
|
|
||||||
val pageRemark = MutableLiveData("") // 备注
|
val pageRemark = MutableLiveData("") // 备注
|
||||||
|
|
||||||
|
// 托盘信息(运抵重量 = 地磅称重 - 托盘数量 × 托盘自重)
|
||||||
|
val trayNumber = MutableLiveData("") // 托盘数量
|
||||||
|
val trayWeight = MutableLiveData("18") // 托盘自重(默认18,可编辑)
|
||||||
|
|
||||||
// 下拉选择数据源
|
// 下拉选择数据源
|
||||||
val channelList = MutableLiveData<List<KeyValue>>() // 通道号列表
|
val channelList = MutableLiveData<List<KeyValue>>() // 通道号列表
|
||||||
val agentList = MutableLiveData<List<KeyValue>>() // 代理人列表
|
val agentList = MutableLiveData<List<KeyValue>>() // 代理人列表
|
||||||
val spCodeList = MutableLiveData<List<KeyValue>>() // 特码列表
|
val spCodeList = MutableLiveData<List<KeyValue>>() // 特码列表
|
||||||
val businessTypeList = MutableLiveData<List<KeyValue>>() // 业务类型列表
|
val businessTypeList = MutableLiveData<List<KeyValue>>() // 业务类型列表
|
||||||
|
|
||||||
// 托盘车自重(平板:托盘车号查询回填只读)
|
// 托盘车自重(查询获取)
|
||||||
val carWeight = MutableLiveData("0")
|
val carWeight = MutableLiveData("0")
|
||||||
|
|
||||||
// ========== 5.5寸手机版专属(平板不调用 initPhoneExtras,行为零变化) ==========
|
|
||||||
|
|
||||||
// 托盘信息(与平板端一致:托盘重量 trayTotalWeight = 托盘数量 × 托盘自重,自重默认 18kg 可编辑)
|
|
||||||
/** 托盘数量 → 入参 trayNumber */
|
|
||||||
val trayNumber = MutableLiveData("")
|
|
||||||
|
|
||||||
/** 单个托盘自重(默认 18kg)→ 与数量相乘得入参 trayTotalWeight */
|
|
||||||
val trayWeight = MutableLiveData("18")
|
|
||||||
|
|
||||||
/** 手机形态标记:为 true 时提交体携带 trayNumber / trayTotalWeight(平板保持原托盘车号链路) */
|
|
||||||
private var phoneMode = false
|
|
||||||
|
|
||||||
/** 托盘重量 = 托盘数量 × 托盘自重 */
|
|
||||||
private fun getTrayTotalWeight(): Double {
|
|
||||||
val num = trayNumber.value?.toIntOrNull() ?: 0
|
|
||||||
val weight = trayWeight.value?.toDoubleOrNull() ?: 0.0
|
|
||||||
return num * weight
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 仅由手机形态的 Activity 调用 */
|
|
||||||
fun initPhoneExtras() {
|
|
||||||
phoneMode = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化
|
* 初始化
|
||||||
*/
|
*/
|
||||||
@@ -106,11 +86,10 @@ class GjcWeighingStartViewModel : BaseViewModel() {
|
|||||||
// 获取传入的运单ID
|
// 获取传入的运单ID
|
||||||
maWbId = intent.getLongExtra(Constant.Key.MAWB_ID, 0)
|
maWbId = intent.getLongExtra(Constant.Key.MAWB_ID, 0)
|
||||||
|
|
||||||
// 监听地磅称重输入,单向同步到运抵重量
|
// 监听地磅称重、托盘数量、托盘自重输入,自动计算运抵重量
|
||||||
diBangWeight.observe(activity as LifecycleOwner) { weight ->
|
diBangWeight.observe(activity as LifecycleOwner) { recalculateArriveWeight() }
|
||||||
// 同步到运抵重量(单向,不做反向同步)
|
trayNumber.observe(activity as LifecycleOwner) { recalculateArriveWeight() }
|
||||||
arriveWeight.value = weight ?: "0"
|
trayWeight.observe(activity as LifecycleOwner) { recalculateArriveWeight() }
|
||||||
}
|
|
||||||
|
|
||||||
// 监听运抵重量变化,自动计算运抵体积
|
// 监听运抵重量变化,自动计算运抵体积
|
||||||
arriveWeight.observe(activity as LifecycleOwner) { weightStr ->
|
arriveWeight.observe(activity as LifecycleOwner) { weightStr ->
|
||||||
@@ -137,6 +116,32 @@ class GjcWeighingStartViewModel : BaseViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动计算运抵重量:地磅称重 - (托盘数量 × 托盘自重)
|
||||||
|
*/
|
||||||
|
private fun recalculateArriveWeight() {
|
||||||
|
val gross = diBangWeight.value?.toDoubleOrNull() ?: 0.0
|
||||||
|
val trayTotal = getTrayTotalWeight()
|
||||||
|
val net = gross - trayTotal
|
||||||
|
arriveWeight.value = if (net > 0) formatWeight(net) else "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 托盘总重量 = 托盘数量 × 托盘自重
|
||||||
|
*/
|
||||||
|
private fun getTrayTotalWeight(): Double {
|
||||||
|
val num = trayNumber.value?.toIntOrNull() ?: 0
|
||||||
|
val weight = trayWeight.value?.toDoubleOrNull() ?: 0.0
|
||||||
|
return num * weight
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重量格式化:最多保留两位小数,去掉多余的0
|
||||||
|
*/
|
||||||
|
private fun formatWeight(value: Double): String {
|
||||||
|
return String.format("%.2f", value).trimEnd('0').trimEnd('.')
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载通道号列表 (国际出港专用)
|
* 加载通道号列表 (国际出港专用)
|
||||||
*/
|
*/
|
||||||
@@ -415,13 +420,11 @@ class GjcWeighingStartViewModel : BaseViewModel() {
|
|||||||
passageWay = this@GjcWeighingStartViewModel.channelList.value?.firstOrNull{ it.value == channel.value }?.key ?: ""
|
passageWay = this@GjcWeighingStartViewModel.channelList.value?.firstOrNull{ it.value == channel.value }?.key ?: ""
|
||||||
passageWayId = this@GjcWeighingStartViewModel.channel.value
|
passageWayId = this@GjcWeighingStartViewModel.channel.value
|
||||||
|
|
||||||
remark = pageRemark.value
|
// 托盘信息
|
||||||
|
trayNumber = this@GjcWeighingStartViewModel.trayNumber.value?.toIntOrNull()
|
||||||
|
trayTotalWeight = getTrayTotalWeight()
|
||||||
|
|
||||||
// 手机版:托盘数量/托盘重量(平板 phoneMode 恒 false,字段为 null 不进请求体)
|
remark = pageRemark.value
|
||||||
if (this@GjcWeighingStartViewModel.phoneMode) {
|
|
||||||
trayNumber = this@GjcWeighingStartViewModel.trayNumber.value?.toIntOrNull()
|
|
||||||
trayTotalWeight = getTrayTotalWeight()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 构建请求数据(GjcMaWb转RequestBody,包含通道号)
|
// 3. 构建请求数据(GjcMaWb转RequestBody,包含通道号)
|
||||||
@@ -447,18 +450,16 @@ class GjcWeighingStartViewModel : BaseViewModel() {
|
|||||||
arrivePc.value = ""
|
arrivePc.value = ""
|
||||||
arriveWeight.value = ""
|
arriveWeight.value = ""
|
||||||
arriveVolume.value = ""
|
arriveVolume.value = ""
|
||||||
|
trayNumber.value = ""
|
||||||
|
trayWeight.value = "18"
|
||||||
|
|
||||||
pageRemark.value = ""
|
pageRemark.value = ""
|
||||||
|
|
||||||
maWbBean.value = maWbBean.value?.apply {
|
maWbBean.value = maWbBean.value?.apply {
|
||||||
carId = null
|
carId = null
|
||||||
remark = null
|
remark = null
|
||||||
trayNumber = null
|
|
||||||
trayTotalWeight = null
|
|
||||||
}
|
}
|
||||||
carWeight.value = "0"
|
carWeight.value = "0"
|
||||||
this@GjcWeighingStartViewModel.trayNumber.value = ""
|
|
||||||
trayWeight.value = "18"
|
|
||||||
} else {
|
} else {
|
||||||
showToast(result.msg.noNull("分托计重失败"))
|
showToast(result.msg.noNull("分托计重失败"))
|
||||||
}
|
}
|
||||||
@@ -533,13 +534,12 @@ class GjcWeighingStartViewModel : BaseViewModel() {
|
|||||||
"goodsCn" to bean.goodsCn,
|
"goodsCn" to bean.goodsCn,
|
||||||
"businessType" to bean.businessType,
|
"businessType" to bean.businessType,
|
||||||
"carId" to bean.carId,
|
"carId" to bean.carId,
|
||||||
|
"trayNumber" to trayNumber.value?.toIntOrNull(),
|
||||||
|
"trayTotalWeight" to getTrayTotalWeight(),
|
||||||
"remark" to pageRemark.value,
|
"remark" to pageRemark.value,
|
||||||
"checkIn" to "1", // 收运状态设置为已收运
|
"checkIn" to "1", // 收运状态设置为已收运
|
||||||
"passageWayId" to channel.value, // 通道号参数
|
"passageWayId" to channel.value, // 通道号参数
|
||||||
"passageWay" to (channelList.value?.firstOrNull{ it.value == channel.value }?.key ?: ""),
|
"passageWay" to (channelList.value?.firstOrNull{ it.value == channel.value }?.key ?: "")
|
||||||
// 手机版托盘信息(平板 phoneMode 恒 false → null → removeEmptyOrNull 移除,请求体不变)
|
|
||||||
"trayNumber" to (if (phoneMode) trayNumber.value?.toIntOrNull() else null),
|
|
||||||
"trayTotalWeight" to (if (phoneMode) getTrayTotalWeight() else null)
|
|
||||||
).toRequestBody(removeEmptyOrNull = true)
|
).toRequestBody(removeEmptyOrNull = true)
|
||||||
|
|
||||||
launchLoadingCollect({
|
launchLoadingCollect({
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class GjcWeighingViewModel : BasePageViewModel() {
|
|||||||
|
|
||||||
// ========== 5.5寸手机版专属字段(平板不调用 initPhoneExtras,行为零变化) ==========
|
// ========== 5.5寸手机版专属字段(平板不调用 initPhoneExtras,行为零变化) ==========
|
||||||
|
|
||||||
/** 手机版顶部搜索关键字(运单号/航班号混输,getData 内分流:含字母→fno,纯数字→likeNo 模糊) */
|
/** 手机版顶部搜索关键字(运单号/航班号混输,getData 内分流:含字母→fno,其余→wbNo) */
|
||||||
val searchKey = MutableLiveData("")
|
val searchKey = MutableLiveData("")
|
||||||
|
|
||||||
/** 主页三格统计:待计重 / 计重中数量(取 pageQueryTotal 出参 waitCheckInNumber / checkingNumber) */
|
/** 主页三格统计:待计重 / 计重中数量(取 pageQueryTotal 出参 waitCheckInNumber / checkingNumber) */
|
||||||
@@ -72,12 +72,11 @@ class GjcWeighingViewModel : BasePageViewModel() {
|
|||||||
private var phoneExtrasEnabled = false
|
private var phoneExtrasEnabled = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开启手机版专属数据:放开「今天」默认日期(设计稿主页默认查全量,日期在筛选弹层可选)。
|
* 开启手机版专属数据:顶部「运单号/航班号」合并搜索框分流(见 getData)。
|
||||||
* 仅由手机形态的 Activity 调用。
|
* 仅由手机形态的 Activity 调用。
|
||||||
*/
|
*/
|
||||||
fun initPhoneExtras() {
|
fun initPhoneExtras() {
|
||||||
phoneExtrasEnabled = true
|
phoneExtrasEnabled = true
|
||||||
flightDate.value = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -253,13 +252,8 @@ class GjcWeighingViewModel : BasePageViewModel() {
|
|||||||
if (phoneExtrasEnabled) {
|
if (phoneExtrasEnabled) {
|
||||||
val key = searchKey.value?.trim() ?: ""
|
val key = searchKey.value?.trim() ?: ""
|
||||||
if (key.isNotEmpty()) {
|
if (key.isNotEmpty()) {
|
||||||
// 分流规则(实测):含字母 → 航班号;11 位数字 → wbNo 精确;
|
// 「运单号/航班号」合并搜索框,只分流到平板同名入参:含字母 → fno,其余 → wbNo
|
||||||
// 其余数字 → likeNo(后端按 8 位 no 匹配,并非任意模糊)
|
if (key.any { it.isLetter() }) baseParams["fno"] = key else baseParams["wbNo"] = key
|
||||||
when {
|
|
||||||
key.any { it.isLetter() } -> baseParams["fno"] = key
|
|
||||||
key.length == 11 -> baseParams["wbNo"] = key
|
|
||||||
else -> baseParams["likeNo"] = key
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import com.lukouguoji.module_base.common.ConstantEvent
|
|||||||
import com.lukouguoji.module_base.http.net.NetApply
|
import com.lukouguoji.module_base.http.net.NetApply
|
||||||
import com.lukouguoji.module_base.impl.FlowBus
|
import com.lukouguoji.module_base.impl.FlowBus
|
||||||
import com.lukouguoji.module_base.ktx.commonAdapter
|
import com.lukouguoji.module_base.ktx.commonAdapter
|
||||||
|
import com.lukouguoji.module_base.ktx.CustomsResponseUrl
|
||||||
|
import com.lukouguoji.module_base.ktx.ITEM_CLICK_TYPE_CUSTOMS_RESPONSE
|
||||||
|
import com.lukouguoji.module_base.ktx.showCustomsResponse
|
||||||
import com.lukouguoji.module_base.ktx.launchCollect
|
import com.lukouguoji.module_base.ktx.launchCollect
|
||||||
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
||||||
import com.lukouguoji.module_base.ktx.showToast
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
@@ -35,6 +38,16 @@ class IntExpArriveViewModel : BasePageViewModel() {
|
|||||||
val flightNo = MutableLiveData("") // 航班号
|
val flightNo = MutableLiveData("") // 航班号
|
||||||
val waybillNo = MutableLiveData("") // 运单号
|
val waybillNo = MutableLiveData("") // 运单号
|
||||||
val hno = MutableLiveData("") // 分单号
|
val hno = MutableLiveData("") // 分单号
|
||||||
|
val arrivalStatus = MutableLiveData("") // 运抵状态(0:未运抵,1:已运抵)
|
||||||
|
|
||||||
|
// 运抵状态下拉列表
|
||||||
|
val arrivalStatusList = MutableLiveData(
|
||||||
|
listOf(
|
||||||
|
KeyValue("全部", ""),
|
||||||
|
KeyValue("已运抵", "1"),
|
||||||
|
KeyValue("未运抵", "0")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// ========== 统计信息 ==========
|
// ========== 统计信息 ==========
|
||||||
val totalCount = MutableLiveData("0") // 合计票数
|
val totalCount = MutableLiveData("0") // 合计票数
|
||||||
@@ -235,6 +248,13 @@ class IntExpArriveViewModel : BasePageViewModel() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showResetDeclareDialog(selectedMaWbList, selectedHaWbList)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态重置弹框 + 请求(平板批量「状态重置」与手机卡片浮层「重置」共用)
|
||||||
|
*/
|
||||||
|
private fun showResetDeclareDialog(selectedMaWbList: List<GjcMaWb>, selectedHaWbList: List<GjcHaWb>) {
|
||||||
// 创建并显示弹框
|
// 创建并显示弹框
|
||||||
val dialog = IntExpArriveResetDialogModel { dialogModel ->
|
val dialog = IntExpArriveResetDialogModel { dialogModel ->
|
||||||
// 弹框确认后的回调
|
// 弹框确认后的回调
|
||||||
@@ -295,6 +315,13 @@ class IntExpArriveViewModel : BasePageViewModel() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteDeclare(selectedMaWbList, selectedHaWbList)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除申报校验 + 弹框 + 请求(平板批量「删除申报」与手机卡片浮层「删除申报」共用)
|
||||||
|
*/
|
||||||
|
private fun deleteDeclare(selectedMaWbList: List<GjcMaWb>, selectedHaWbList: List<GjcHaWb>) {
|
||||||
// 校验申报状态:只有申报状态为 01 的记录才能删除申报
|
// 校验申报状态:只有申报状态为 01 的记录才能删除申报
|
||||||
if (selectedMaWbList.any { it.declareStatus != "01" } ||
|
if (selectedMaWbList.any { it.declareStatus != "01" } ||
|
||||||
selectedHaWbList.any { it.arrivalStatus != "01" }
|
selectedHaWbList.any { it.arrivalStatus != "01" }
|
||||||
@@ -390,23 +417,15 @@ class IntExpArriveViewModel : BasePageViewModel() {
|
|||||||
*/
|
*/
|
||||||
fun resetSingleDeclareClick(bean: GjcMaWb) {
|
fun resetSingleDeclareClick(bean: GjcMaWb) {
|
||||||
bean.actionMenuVisible.set(false)
|
bean.actionMenuVisible.set(false)
|
||||||
|
showResetDeclareDialog(listOf(bean), emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
val dialog = IntExpArriveResetDialogModel { dialogModel ->
|
/**
|
||||||
val params = mutableMapOf<String, Any?>("maWbList" to listOf(bean))
|
* 手机版:卡片长按菜单「删除申报」(单条,对应平板批量「删除申报」)。
|
||||||
if (dialogModel.resetStatusCode != null) {
|
*/
|
||||||
params["restStatus"] = dialogModel.resetStatusCode
|
fun deleteSingleDeclareClick(bean: GjcMaWb) {
|
||||||
}
|
bean.actionMenuVisible.set(false)
|
||||||
launchLoadingCollect({ NetApply.api.resetArriveDeclare(params.toRequestBody()) }) {
|
deleteDeclare(listOf(bean), emptyList())
|
||||||
onSuccess = {
|
|
||||||
showToast("状态重置成功")
|
|
||||||
viewModelScope.launch {
|
|
||||||
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
|
|
||||||
}
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dialog.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -418,19 +437,10 @@ class IntExpArriveViewModel : BasePageViewModel() {
|
|||||||
"fdate" to flightDate.value?.ifEmpty { null },
|
"fdate" to flightDate.value?.ifEmpty { null },
|
||||||
"fno" to flightNo.value?.ifEmpty { null },
|
"fno" to flightNo.value?.ifEmpty { null },
|
||||||
"wbNo" to waybillNo.value?.ifEmpty { null },
|
"wbNo" to waybillNo.value?.ifEmpty { null },
|
||||||
"hno" to hno.value?.ifEmpty { null }
|
"hno" to hno.value?.ifEmpty { null },
|
||||||
|
"arrivalStatus" to arrivalStatus.value?.ifEmpty { null }
|
||||||
)
|
)
|
||||||
|
|
||||||
// 手机版专属条件(平板 phoneExtrasEnabled 恒为 false,请求体与改造前完全一致)
|
|
||||||
if (phoneExtrasEnabled) {
|
|
||||||
// 搜索分流(与出港计重页一致):11 位数字精确匹配 wbNo,其余走 likeNo 模糊
|
|
||||||
val key = waybillNo.value?.trim() ?: ""
|
|
||||||
if (key.isNotEmpty() && key.length != 11) {
|
|
||||||
filterParams["wbNo"] = null
|
|
||||||
filterParams["likeNo"] = key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 手机版 Tab 维度(运抵状态),角标需在不含该条件的基础上分别统计
|
// 手机版 Tab 维度(运抵状态),角标需在不含该条件的基础上分别统计
|
||||||
val tabParams = if (phoneExtrasEnabled) {
|
val tabParams = if (phoneExtrasEnabled) {
|
||||||
filterParams + mapOf("arrivalStatus" to currentArrivalStatus())
|
filterParams + mapOf("arrivalStatus" to currentArrivalStatus())
|
||||||
@@ -475,4 +485,19 @@ class IntExpArriveViewModel : BasePageViewModel() {
|
|||||||
loadPhoneTabCounts(filterParams)
|
loadPhoneTabCounts(filterParams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表项点击分发:侧滑「回执」按钮 → 查询海关回执并跳转只读详情页
|
||||||
|
*/
|
||||||
|
override fun onItemClick(position: Int, type: Int) {
|
||||||
|
val bean = pageModel.rv?.commonAdapter()?.getItem(position) as? GjcMaWb ?: return
|
||||||
|
if (type == ITEM_CLICK_TYPE_CUSTOMS_RESPONSE) {
|
||||||
|
queryCustomsResponse(bean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询海关回执并跳转只读回执页(平板侧滑「回执」与手机长按浮层「回执」共用) */
|
||||||
|
fun queryCustomsResponse(bean: GjcMaWb) {
|
||||||
|
showCustomsResponse(CustomsResponseUrl.INT_EXP_ARRIVE, bean.prefix, bean.no, "出港运抵回执")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
|||||||
import com.lukouguoji.module_base.ktx.showToast
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
import com.lukouguoji.module_base.ktx.toRequestBody
|
import com.lukouguoji.module_base.ktx.toRequestBody
|
||||||
import com.lukouguoji.module_base.model.ScanModel
|
import com.lukouguoji.module_base.model.ScanModel
|
||||||
|
import com.lukouguoji.module_base.util.DictUtils
|
||||||
import dev.utils.app.info.KeyValue
|
import dev.utils.app.info.KeyValue
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -75,6 +76,12 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
// ========== 组装人列表 ==========
|
// ========== 组装人列表 ==========
|
||||||
val assemblerList = MutableLiveData<List<KeyValue>>(emptyList())
|
val assemblerList = MutableLiveData<List<KeyValue>>(emptyList())
|
||||||
|
|
||||||
|
// ========== BUP选项(0:否;1:是),默认否 ==========
|
||||||
|
val bupFlagList = listOf(KeyValue("否", "0"), KeyValue("是", "1"))
|
||||||
|
|
||||||
|
// ========== 板型选项(字典;支持下拉选择,也支持手动输入)==========
|
||||||
|
val boardTypeList = MutableLiveData<List<KeyValue>>(emptyList())
|
||||||
|
|
||||||
// ========== 标记位,避免重复查询 ==========
|
// ========== 标记位,避免重复查询 ==========
|
||||||
private var lastQueriedUldNo = ""
|
private var lastQueriedUldNo = ""
|
||||||
|
|
||||||
@@ -107,6 +114,12 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
*/
|
*/
|
||||||
val isLoadEnabled = MutableLiveData(true)
|
val isLoadEnabled = MutableLiveData(true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本页面是否已至少一次装货成功
|
||||||
|
* 首次装货成功前,运单列表件数/重量列显示总数;成功后显示“剩余/总数”
|
||||||
|
*/
|
||||||
|
val hasLoadedOnce = MutableLiveData(false)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 卸货按钮启用状态(编辑模式时启用)
|
* 卸货按钮启用状态(编辑模式时启用)
|
||||||
*/
|
*/
|
||||||
@@ -274,6 +287,27 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
ScanModel.startScan(getTopActivity(), Constant.RequestCode.WAYBILL)
|
ScanModel.startScan(getTopActivity(), Constant.RequestCode.WAYBILL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待组装运单接口数据转右侧运单列表 Bean
|
||||||
|
* 同时带上已组装件数/重量与“剩余/总数”显示开关(首次装货成功后生效)
|
||||||
|
*/
|
||||||
|
private fun GjcWarehouse.toAssembleWaybillBean(): AssembleWaybillBean {
|
||||||
|
val warehouse = this
|
||||||
|
return AssembleWaybillBean().apply {
|
||||||
|
waybillNo = warehouse.wbNo
|
||||||
|
pieces = warehouse.pc.toString()
|
||||||
|
weight = String.format("%.1f", warehouse.weight)
|
||||||
|
flight = warehouse.flight
|
||||||
|
fno = warehouse.fno
|
||||||
|
fdate = warehouse.fdate
|
||||||
|
whId = warehouse.whId // 运单ID(用于接口调用)
|
||||||
|
isMarked = false
|
||||||
|
checkInPieces = warehouse.checkInPc
|
||||||
|
checkInWeight = warehouse.checkInWeight
|
||||||
|
showRemaining = hasLoadedOnce.value == true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载待组装运单列表
|
* 加载待组装运单列表
|
||||||
*/
|
*/
|
||||||
@@ -285,18 +319,7 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
onSuccess = { result ->
|
onSuccess = { result ->
|
||||||
// 数据转换: GjcWarehouse -> AssembleWaybillBean
|
// 数据转换: GjcWarehouse -> AssembleWaybillBean
|
||||||
val warehouseList = result.data ?: mutableListOf()
|
val warehouseList = result.data ?: mutableListOf()
|
||||||
val waybillBeanList = warehouseList.map { warehouse ->
|
val waybillBeanList = warehouseList.map { it.toAssembleWaybillBean() }.toMutableList()
|
||||||
AssembleWaybillBean().apply {
|
|
||||||
waybillNo = warehouse.wbNo
|
|
||||||
pieces = warehouse.pc.toString()
|
|
||||||
weight = String.format("%.1f", warehouse.weight)
|
|
||||||
flight = warehouse.flight
|
|
||||||
fno = warehouse.fno
|
|
||||||
fdate = warehouse.fdate
|
|
||||||
whId = warehouse.whId // 添加运单ID
|
|
||||||
isMarked = false
|
|
||||||
}
|
|
||||||
}.toMutableList()
|
|
||||||
|
|
||||||
// 更新列表
|
// 更新列表
|
||||||
waybillList.value = waybillBeanList
|
waybillList.value = waybillBeanList
|
||||||
@@ -335,18 +358,7 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
launchLoadingCollect({ NetApply.api.queryWaitingAssemble("") }) {
|
launchLoadingCollect({ NetApply.api.queryWaitingAssemble("") }) {
|
||||||
onSuccess = { result ->
|
onSuccess = { result ->
|
||||||
val warehouseList = result.data ?: mutableListOf()
|
val warehouseList = result.data ?: mutableListOf()
|
||||||
val waybillBeanList = warehouseList.map { warehouse ->
|
val waybillBeanList = warehouseList.map { it.toAssembleWaybillBean() }.toMutableList()
|
||||||
AssembleWaybillBean().apply {
|
|
||||||
waybillNo = warehouse.wbNo
|
|
||||||
pieces = warehouse.pc.toString()
|
|
||||||
weight = String.format("%.1f", warehouse.weight)
|
|
||||||
flight = warehouse.flight
|
|
||||||
fno = warehouse.fno
|
|
||||||
fdate = warehouse.fdate
|
|
||||||
whId = warehouse.whId // 添加运单ID
|
|
||||||
isMarked = false
|
|
||||||
}
|
|
||||||
}.toMutableList()
|
|
||||||
|
|
||||||
waybillList.value = waybillBeanList
|
waybillList.value = waybillBeanList
|
||||||
|
|
||||||
@@ -399,6 +411,29 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载板型选项列表
|
||||||
|
* @param carrier 承运人二字码(ULD 编号后两位);进入页面时不传取全量,ULD 信息查询成功后按承运人重新加载并覆盖数据源
|
||||||
|
* @param autoSelectFirst 查询结果不为空时是否自动选中第一项回填到板型输入框;仅 ULD 触发的按承运人查询传 true,进入页面的全量查询不自动选择
|
||||||
|
*/
|
||||||
|
fun loadBoardTypeList(carrier: String? = null, autoSelectFirst: Boolean = false) {
|
||||||
|
DictUtils.getBoardTypeList(carrier) {
|
||||||
|
boardTypeList.value = it
|
||||||
|
if (autoSelectFirst && it.isNotEmpty()) onBoardTypeSelected(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 板型下拉选中回调:回填到板型输入框(回填后仍可手动修改)
|
||||||
|
* 下拉列表由 Activity 以锚定在输入框下方的 ListPopupWindow 弹出(外观与 BUP 的 Spinner 一致)
|
||||||
|
*/
|
||||||
|
fun onBoardTypeSelected(position: Int) {
|
||||||
|
val list = boardTypeList.value ?: emptyList()
|
||||||
|
uldInfo.value = uldInfo.value?.apply {
|
||||||
|
boardType = list.getOrNull(position)?.value ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ULD编号输入完成时调用
|
* ULD编号输入完成时调用
|
||||||
*/
|
*/
|
||||||
@@ -433,8 +468,16 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
}
|
}
|
||||||
// 保存useId(用于装货/卸货接口)
|
// 保存useId(用于装货/卸货接口)
|
||||||
useId = uldBean.useId
|
useId = uldBean.useId
|
||||||
|
// 回填 IMP代码 / 板型 / BUP:服务端有值时覆盖(修改模式回显),为空时保留当前输入
|
||||||
|
if (!uldBean.dgrCode.isNullOrEmpty()) dgrCode = uldBean.dgrCode
|
||||||
|
if (!uldBean.boardType.isNullOrEmpty()) boardType = uldBean.boardType
|
||||||
|
if (!uldBean.bupFlag.isNullOrEmpty()) bupFlag = uldBean.bupFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按 ULD 编号后两位(承运人二字码)重新加载板型选项,覆盖下拉数据源
|
||||||
|
val carrier = uldNo.trim().takeLast(2)
|
||||||
|
if (carrier.length == 2) loadBoardTypeList(carrier, autoSelectFirst = true)
|
||||||
|
|
||||||
// 【新增】从ULD信息提取航班信息
|
// 【新增】从ULD信息提取航班信息
|
||||||
if (uldBean.fno.isNotEmpty() && uldBean.fdate.isNotEmpty()) {
|
if (uldBean.fno.isNotEmpty() && uldBean.fdate.isNotEmpty()) {
|
||||||
assembleFlightNo.value = uldBean.fno
|
assembleFlightNo.value = uldBean.fno
|
||||||
@@ -601,9 +644,17 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
|
|
||||||
val loadAreaId = selectedPosition.value?.positionCode?.trim() ?: ""
|
val loadAreaId = selectedPosition.value?.positionCode?.trim() ?: ""
|
||||||
|
|
||||||
|
// IMP代码 / 板型 / BUP(接口新增字段,useInfo 与顶层参数均需携带)
|
||||||
|
val dgrCode = uldInfo.value?.dgrCode?.trim() ?: ""
|
||||||
|
val boardType = uldInfo.value?.boardType?.trim() ?: ""
|
||||||
|
val bupFlag = uldInfo.value?.bupFlag?.ifEmpty { "0" } ?: "0"
|
||||||
|
|
||||||
// 2. 构建useInfo(ULD信息)
|
// 2. 构建useInfo(ULD信息)
|
||||||
val useInfo = mapOf(
|
val useInfo = mapOf(
|
||||||
"uld" to uldNo,
|
"uld" to uldNo,
|
||||||
|
"dgrCode" to dgrCode.ifEmpty { null },
|
||||||
|
"boardType" to boardType.ifEmpty { null },
|
||||||
|
"bupFlag" to bupFlag,
|
||||||
"consumeWeight" to materialWeight.toDoubleOrNull(),
|
"consumeWeight" to materialWeight.toDoubleOrNull(),
|
||||||
"status" to when (uldInfo.value?.uldStatus) {
|
"status" to when (uldInfo.value?.uldStatus) {
|
||||||
"正常" -> "0"
|
"正常" -> "0"
|
||||||
@@ -688,6 +739,9 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
"ldId" to operatorValue,
|
"ldId" to operatorValue,
|
||||||
"loadArea" to loadArea,
|
"loadArea" to loadArea,
|
||||||
"loadAreaId" to loadAreaId.ifEmpty { null }, // 组装位置ID
|
"loadAreaId" to loadAreaId.ifEmpty { null }, // 组装位置ID
|
||||||
|
"dgrCode" to dgrCode.ifEmpty { null }, // IMP代码
|
||||||
|
"boardType" to boardType.ifEmpty { null }, // 板型
|
||||||
|
"bupFlag" to bupFlag, // BUP选项(0:否;1:是)
|
||||||
"useInfo" to useInfo,
|
"useInfo" to useInfo,
|
||||||
"wbInfo" to wbInfo,
|
"wbInfo" to wbInfo,
|
||||||
"userId" to SharedPreferenceUtil.getString(Constant.Share.account)
|
"userId" to SharedPreferenceUtil.getString(Constant.Share.account)
|
||||||
@@ -724,6 +778,11 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
) {
|
) {
|
||||||
showToast("${operationName}成功")
|
showToast("${operationName}成功")
|
||||||
|
|
||||||
|
// 首次装货成功后,运单列表件数/重量列切换为“剩余/总数”显示
|
||||||
|
if (isLoad) {
|
||||||
|
hasLoadedOnce.value = true
|
||||||
|
}
|
||||||
|
|
||||||
// 发送刷新事件,通知出港组装列表页面更新数据
|
// 发送刷新事件,通知出港组装列表页面更新数据
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
|
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
|
||||||
@@ -780,18 +839,7 @@ class IntExpAssembleStartViewModel : BaseViewModel() {
|
|||||||
launchLoadingCollect({ NetApply.api.queryWaitingAssemble(wbNo) }) {
|
launchLoadingCollect({ NetApply.api.queryWaitingAssemble(wbNo) }) {
|
||||||
onSuccess = { result ->
|
onSuccess = { result ->
|
||||||
val warehouseList = result.data ?: mutableListOf()
|
val warehouseList = result.data ?: mutableListOf()
|
||||||
val waybillBeanList = warehouseList.map { warehouse ->
|
val waybillBeanList = warehouseList.map { it.toAssembleWaybillBean() }.toMutableList()
|
||||||
AssembleWaybillBean().apply {
|
|
||||||
waybillNo = warehouse.wbNo
|
|
||||||
pieces = warehouse.pc.toString()
|
|
||||||
weight = String.format("%.1f", warehouse.weight)
|
|
||||||
flight = warehouse.flight
|
|
||||||
fno = warehouse.fno
|
|
||||||
fdate = warehouse.fdate
|
|
||||||
whId = warehouse.whId
|
|
||||||
isMarked = false
|
|
||||||
}
|
|
||||||
}.toMutableList()
|
|
||||||
|
|
||||||
waybillList.value = waybillBeanList
|
waybillList.value = waybillBeanList
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import com.lukouguoji.module_base.common.ConstantEvent
|
|||||||
import com.lukouguoji.module_base.http.net.NetApply
|
import com.lukouguoji.module_base.http.net.NetApply
|
||||||
import com.lukouguoji.module_base.impl.FlowBus
|
import com.lukouguoji.module_base.impl.FlowBus
|
||||||
import com.lukouguoji.module_base.ktx.commonAdapter
|
import com.lukouguoji.module_base.ktx.commonAdapter
|
||||||
|
import com.lukouguoji.module_base.ktx.CustomsResponseUrl
|
||||||
|
import com.lukouguoji.module_base.ktx.ITEM_CLICK_TYPE_CUSTOMS_RESPONSE
|
||||||
|
import com.lukouguoji.module_base.ktx.showCustomsResponse
|
||||||
import com.lukouguoji.module_base.ktx.launchCollect
|
import com.lukouguoji.module_base.ktx.launchCollect
|
||||||
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
||||||
import com.lukouguoji.module_base.ktx.noNull
|
import com.lukouguoji.module_base.ktx.noNull
|
||||||
@@ -310,4 +313,14 @@ class IntExpLoadViewModel : BasePageViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表项点击分发:侧滑「回执」按钮 → 查询海关回执并跳转只读详情页
|
||||||
|
*/
|
||||||
|
override fun onItemClick(position: Int, type: Int) {
|
||||||
|
val bean = pageModel.rv?.commonAdapter()?.getItem(position) as? GjcExportLoad ?: return
|
||||||
|
if (type == ITEM_CLICK_TYPE_CUSTOMS_RESPONSE) {
|
||||||
|
showCustomsResponse(CustomsResponseUrl.INT_EXP_LOAD, bean.prefix, bean.no, "出港装载回执")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package com.lukouguoji.gjc.viewModel
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.lukouguoji.module_base.base.BaseViewModel
|
|
||||||
import com.lukouguoji.module_base.bean.GjcMove
|
|
||||||
import com.lukouguoji.module_base.common.Constant
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 国际出港移库「运单详情」ViewModel(手机版专属页面)
|
|
||||||
*
|
|
||||||
* 列表项数据已包含详情页所需全部字段,故直接由列表带入,不额外请求接口。
|
|
||||||
*/
|
|
||||||
class IntExpMoveDetailViewModel : BaseViewModel() {
|
|
||||||
|
|
||||||
val dataBean = MutableLiveData(GjcMove())
|
|
||||||
|
|
||||||
fun initOnCreated(intent: Intent) {
|
|
||||||
val json = intent.getStringExtra(Constant.Key.DATA).orEmpty()
|
|
||||||
if (json.isNotEmpty()) {
|
|
||||||
dataBean.value = Gson().fromJson(json, GjcMove::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package com.lukouguoji.gjc.viewModel
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.lukouguoji.gjc.R
|
||||||
|
import com.lukouguoji.module_base.base.BaseViewModel
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.lukouguoji.module_base.bean.FileBean
|
||||||
|
import com.lukouguoji.module_base.bean.GjcMove
|
||||||
|
import com.lukouguoji.module_base.bean.GjcMoveDetail
|
||||||
|
import com.lukouguoji.module_base.common.Constant
|
||||||
|
import com.lukouguoji.module_base.common.ConstantEvent
|
||||||
|
import com.lukouguoji.module_base.common.DetailsPageType
|
||||||
|
import com.lukouguoji.module_base.http.net.NetApply
|
||||||
|
import com.lukouguoji.module_base.impl.FlowBus
|
||||||
|
import com.lukouguoji.module_base.impl.ImageSelectNewViewHolder
|
||||||
|
import com.lukouguoji.module_base.interfaces.IOnItemClickListener
|
||||||
|
import com.lukouguoji.module_base.ktx.commonAdapter
|
||||||
|
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
||||||
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
|
import com.lukouguoji.module_base.ktx.toRequestBody
|
||||||
|
import com.lukouguoji.module_base.util.MediaUtil
|
||||||
|
import com.lukouguoji.module_base.util.UploadUtil
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际出港移库编辑/详情 ViewModel
|
||||||
|
* 详情:/IntExpMove/detail?maWbId= 返回 GjcMoveDetail
|
||||||
|
* 编辑:/IntExpMove/update 请求体为 GjcMoveDetail,仅允许修改备注与交接图片
|
||||||
|
*/
|
||||||
|
class IntExpMoveEditViewModel : BaseViewModel(), IOnItemClickListener {
|
||||||
|
|
||||||
|
val pageType = MutableLiveData(DetailsPageType.Details)
|
||||||
|
private var maWbId: Long = 0L
|
||||||
|
|
||||||
|
// 列表页传入的运单数据,用于回填 detail 接口返回为空的展示字段(承运人、出运路径等);
|
||||||
|
// 手机布局另用它展示移库人/移库时间(initOnCreated 在绑定前完成赋值)
|
||||||
|
var listBean: GjcMove? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
val dataBean = MutableLiveData(GjcMoveDetail())
|
||||||
|
|
||||||
|
// 备注单独用 LiveData 双向绑定,避免修改 data class 属性后需重新赋值
|
||||||
|
val remark = MutableLiveData("")
|
||||||
|
|
||||||
|
// 图片列表
|
||||||
|
val imageList = MutableLiveData<MutableList<FileBean>>(mutableListOf())
|
||||||
|
|
||||||
|
// 图片适配器配置
|
||||||
|
val imageItemLayoutId = R.layout.item_image_select_new
|
||||||
|
val imageItemViewHolder = ImageSelectNewViewHolder::class.java
|
||||||
|
|
||||||
|
// 详情模式下无图片时显示占位提示
|
||||||
|
val showNoImage = MutableLiveData(false)
|
||||||
|
|
||||||
|
// 图片 RecyclerView 引用(用于从 adapter 实时获取图片)
|
||||||
|
var rvImages: RecyclerView? = null
|
||||||
|
|
||||||
|
fun initOnCreated(intent: Intent) {
|
||||||
|
pageType.value = DetailsPageType.valueOf(
|
||||||
|
intent.getStringExtra(Constant.Key.PAGE_TYPE) ?: DetailsPageType.Details.name
|
||||||
|
)
|
||||||
|
maWbId = intent.getStringExtra(Constant.Key.ID)?.toLongOrNull() ?: 0L
|
||||||
|
listBean = intent.getStringExtra(Constant.Key.DATA)
|
||||||
|
?.takeIf { it.isNotEmpty() }
|
||||||
|
?.let { runCatching { Gson().fromJson(it, GjcMove::class.java) }.getOrNull() }
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadData() {
|
||||||
|
launchLoadingCollect({ NetApply.api.getIntExpMoveDetail(maWbId) }) {
|
||||||
|
onSuccess = {
|
||||||
|
val bean = it.data ?: GjcMoveDetail()
|
||||||
|
if (bean.maWbId == null) bean.maWbId = maWbId
|
||||||
|
fillFromListBean(bean)
|
||||||
|
dataBean.value = bean
|
||||||
|
remark.value = bean.remark ?: ""
|
||||||
|
|
||||||
|
// 解析图片:pic 为缩略图、originalPic 为原图,二者按逗号分隔且一一对应
|
||||||
|
val picList = (bean.pic ?: "").split(",").filter { p -> p.isNotEmpty() }
|
||||||
|
val originalPicList = (bean.originalPic ?: "").split(",").filter { p -> p.isNotEmpty() }
|
||||||
|
val images = if (picList.isNotEmpty()) {
|
||||||
|
picList.mapIndexed { index, picUrl ->
|
||||||
|
val originalUrl = originalPicList.getOrElse(index) { picUrl }
|
||||||
|
FileBean(
|
||||||
|
path = MediaUtil.fillUrl(originalUrl),
|
||||||
|
url = picUrl,
|
||||||
|
originalPic = originalUrl
|
||||||
|
)
|
||||||
|
}.toMutableList()
|
||||||
|
} else {
|
||||||
|
originalPicList.map { url ->
|
||||||
|
FileBean(path = MediaUtil.fillUrl(url), url = url, originalPic = url)
|
||||||
|
}.toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑模式添加空 FileBean 用于显示“添加照片”按钮
|
||||||
|
if (pageType.value == DetailsPageType.Modify) {
|
||||||
|
images.add(FileBean())
|
||||||
|
}
|
||||||
|
|
||||||
|
imageList.value = images
|
||||||
|
|
||||||
|
// 详情模式下无图片时显示占位提示
|
||||||
|
showNoImage.value = pageType.value == DetailsPageType.Details && images.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* detail 接口的部分展示字段(承运人 by、出运路径 range 等)在测试环境返回 null,
|
||||||
|
* 列表接口有值时用列表数据回填,仅影响展示
|
||||||
|
*/
|
||||||
|
private fun fillFromListBean(bean: GjcMoveDetail) {
|
||||||
|
val src = listBean ?: return
|
||||||
|
if (bean.wbNo.isNullOrEmpty()) bean.wbNo = src.wbNo.ifEmpty { src.prefix + src.no }
|
||||||
|
if (bean.by.isNullOrEmpty()) bean.by = src.by1
|
||||||
|
if (bean.range.isNullOrEmpty()) bean.range = src.range
|
||||||
|
if (bean.spCode.isNullOrEmpty()) bean.spCode = src.spCode
|
||||||
|
if (bean.agentCode.isNullOrEmpty()) bean.agentCode = src.agentCode
|
||||||
|
if (bean.awbTypeName.isNullOrEmpty()) bean.awbTypeName = src.awbTypeName
|
||||||
|
if (bean.goods.isNullOrEmpty()) bean.goods = src.goodsCn.ifEmpty { src.goods }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交保存:按接口文档回传完整详情对象,仅备注与交接图片三字段取页面上的新值
|
||||||
|
*/
|
||||||
|
fun submit() {
|
||||||
|
// 从 adapter 实时获取所有非空图片(ViewHolder 新增图片直接操作 adapter items)
|
||||||
|
val allItems = (rvImages?.commonAdapter()?.items as? List<*>)
|
||||||
|
?.filterIsInstance<FileBean>() ?: imageList.value!!
|
||||||
|
val images = allItems.filter { it.path.isNotEmpty() }
|
||||||
|
|
||||||
|
if (images.size > 7) {
|
||||||
|
showToast("最多上传7张图片")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
launchLoadingCollect({
|
||||||
|
// 上传本地新图片;已上传的图片保持原有 url 与 originalPic
|
||||||
|
images.forEach { fileBean ->
|
||||||
|
if (fileBean.url.isEmpty()) {
|
||||||
|
// UploadUtil 返回:newName=原图(较大),zipFileName=缩略图(较小)
|
||||||
|
// FileBean.url 用作缩略图标识,FileBean.originalPic 用作原图标识
|
||||||
|
val data = UploadUtil.upload(fileBean.path).data
|
||||||
|
fileBean.url = data?.zipFileName ?: ""
|
||||||
|
fileBean.originalPic = data?.newName ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val bean = (dataBean.value ?: GjcMoveDetail()).copy(
|
||||||
|
maWbId = maWbId,
|
||||||
|
remark = remark.value ?: "",
|
||||||
|
picNumber = images.size.toString(),
|
||||||
|
pic = images.joinToString(",") { MediaUtil.removeUrl(it.url) },
|
||||||
|
originalPic = images.joinToString(",") { MediaUtil.removeUrl(it.originalPic) }
|
||||||
|
)
|
||||||
|
|
||||||
|
NetApply.api.updateIntExpMove(bean.toRequestBody())
|
||||||
|
}) {
|
||||||
|
onSuccess = {
|
||||||
|
showToast("保存成功")
|
||||||
|
viewModelScope.launch {
|
||||||
|
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
|
||||||
|
}
|
||||||
|
getTopActivity().finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
getTopActivity().finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理图片删除点击事件
|
||||||
|
*/
|
||||||
|
override fun onItemClick(position: Int, type: Int) {
|
||||||
|
if (type == R.id.iv_delete) {
|
||||||
|
val adapter = rvImages?.commonAdapter() ?: return
|
||||||
|
if (position < adapter.items.size) {
|
||||||
|
adapter.removeItem(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,11 +30,6 @@ class IntExpMoveViewModel : BasePageViewModel(), IOnItemClickListener {
|
|||||||
val waybillNo = MutableLiveData("") // 运单号
|
val waybillNo = MutableLiveData("") // 运单号
|
||||||
|
|
||||||
// ========== 手机版(5.5 寸手持端)专用 ==========
|
// ========== 手机版(5.5 寸手持端)专用 ==========
|
||||||
// 平板布局无对应控件,spCode 恒为空 → 不进请求,平板查询行为与改造前一致
|
|
||||||
val spCode = MutableLiveData("") // 特码
|
|
||||||
|
|
||||||
/** 特码下拉数据(仅手机版筛选弹层使用,由 [initPhoneExtras] 触发加载) */
|
|
||||||
val spCodeList = MutableLiveData<List<KeyValue>>(emptyList())
|
|
||||||
|
|
||||||
/** 手机版状态 Tab 配置(设计稿只有两个 Tab,无「全部」) */
|
/** 手机版状态 Tab 配置(设计稿只有两个 Tab,无「全部」) */
|
||||||
val moveStateTabs = MutableLiveData(
|
val moveStateTabs = MutableLiveData(
|
||||||
@@ -48,23 +43,18 @@ class IntExpMoveViewModel : BasePageViewModel(), IOnItemClickListener {
|
|||||||
val selectedCount = MutableLiveData("0")
|
val selectedCount = MutableLiveData("0")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否启用手机版专属数据(Tab 角标计数、特码下拉)。
|
* 是否启用手机版专属数据(Tab 角标计数)。
|
||||||
* 平板端保持 false,不产生任何额外接口请求。
|
* 平板端保持 false,不产生任何额外接口请求。
|
||||||
*/
|
*/
|
||||||
private var phoneExtrasEnabled = false
|
private var phoneExtrasEnabled = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开启手机版专属数据:默认停留「待移库」Tab、加载特码下拉、启用 Tab 角标计数。
|
* 开启手机版专属数据:默认停留「待移库」Tab、启用 Tab 角标计数。
|
||||||
* 仅由手机形态的 Activity 调用。
|
* 仅由手机形态的 Activity 调用。
|
||||||
*/
|
*/
|
||||||
fun initPhoneExtras() {
|
fun initPhoneExtras() {
|
||||||
phoneExtrasEnabled = true
|
phoneExtrasEnabled = true
|
||||||
moveState.value = "0"
|
moveState.value = "0"
|
||||||
// flag=1 国际;ieFlag 必须留空——传 "E" 后端返回空数组,
|
|
||||||
// 与 GjcWeighingStartViewModel 等既有国际出港页面的调用方式保持一致
|
|
||||||
DictUtils.getSpecialCodeList(addAll = false, flag = 1, ieFlag = "") { list ->
|
|
||||||
spCodeList.postValue(list)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 运单类型下拉数据 ==========
|
// ========== 运单类型下拉数据 ==========
|
||||||
@@ -196,8 +186,6 @@ class IntExpMoveViewModel : BasePageViewModel(), IOnItemClickListener {
|
|||||||
dest1.value?.takeIf { it.isNotEmpty() }?.let { filterParams["dest1"] = it }
|
dest1.value?.takeIf { it.isNotEmpty() }?.let { filterParams["dest1"] = it }
|
||||||
moveState.value?.takeIf { it.isNotEmpty() }?.let { filterParams["moveState"] = it }
|
moveState.value?.takeIf { it.isNotEmpty() }?.let { filterParams["moveState"] = it }
|
||||||
waybillNo.value?.takeIf { it.isNotEmpty() }?.let { filterParams["wbNo"] = it }
|
waybillNo.value?.takeIf { it.isNotEmpty() }?.let { filterParams["wbNo"] = it }
|
||||||
// 手机版新增筛选维度,平板端恒为空故不参与查询
|
|
||||||
spCode.value?.takeIf { it.isNotEmpty() }?.let { filterParams["spCode"] = it }
|
|
||||||
|
|
||||||
// 列表参数(含分页)
|
// 列表参数(含分页)
|
||||||
val listParams = (filterParams + mapOf(
|
val listParams = (filterParams + mapOf(
|
||||||
|
|||||||
@@ -147,6 +147,36 @@ class IntExpOutHandoverViewModel : BasePageViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许装机(多选,批量操作)
|
||||||
|
*/
|
||||||
|
fun allowLoadClick() {
|
||||||
|
val list = pageModel.rv?.commonAdapter()?.items as? List<GjcUldUseBean> ?: return
|
||||||
|
val selectedItems = list.filter { it.isSelected }
|
||||||
|
|
||||||
|
if (selectedItems.isEmpty()) {
|
||||||
|
showToast("请选择要允许装机的ULD")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedItems.all { it.isAllowLoad }) {
|
||||||
|
showToast("所选ULD均已允许装机")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val requestData = selectedItems.toRequestBody()
|
||||||
|
|
||||||
|
launchLoadingCollect({ NetApply.api.allowLoading(requestData) }) {
|
||||||
|
onSuccess = {
|
||||||
|
showToast("允许装机成功")
|
||||||
|
viewModelScope.launch {
|
||||||
|
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
|
||||||
|
}
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 待配运按钮点击
|
* 待配运按钮点击
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -186,9 +186,10 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
|
|||||||
/**
|
/**
|
||||||
* 执行清仓操作
|
* 执行清仓操作
|
||||||
* @param clearNormal 清仓正常("0"或"1")
|
* @param clearNormal 清仓正常("0"或"1")
|
||||||
|
* @param clearRemark 清仓备注(选填)
|
||||||
* @param maWbListForClear 包含选中子列表项的主列表数据
|
* @param maWbListForClear 包含选中子列表项的主列表数据
|
||||||
*/
|
*/
|
||||||
fun performClear(clearNormal: String, maWbListForClear: List<GjcMaWb>) {
|
fun performClear(clearNormal: String, clearRemark: String, maWbListForClear: List<GjcMaWb>) {
|
||||||
if (maWbListForClear.isEmpty()) {
|
if (maWbListForClear.isEmpty()) {
|
||||||
showToast("请至少选择一个库位")
|
showToast("请至少选择一个库位")
|
||||||
return
|
return
|
||||||
@@ -197,6 +198,7 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
|
|||||||
// 构建请求参数:完整的主子列表结构
|
// 构建请求参数:完整的主子列表结构
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"clearNormal" to clearNormal,
|
"clearNormal" to clearNormal,
|
||||||
|
"clearRemark" to clearRemark.ifEmpty { null },
|
||||||
"maWbList" to maWbListForClear
|
"maWbList" to maWbListForClear
|
||||||
).toRequestBody()
|
).toRequestBody()
|
||||||
|
|
||||||
@@ -269,16 +271,26 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行出库操作
|
* 执行出库操作
|
||||||
|
* @param pc 出库件数
|
||||||
|
* @param weight 出库重量
|
||||||
* @param selectedStorageList 选中的子列表项(库位)
|
* @param selectedStorageList 选中的子列表项(库位)
|
||||||
*/
|
*/
|
||||||
fun performOutStorage(selectedStorageList: List<com.lukouguoji.module_base.bean.GjcStorageUse>) {
|
fun performOutStorage(
|
||||||
|
pc: Long?,
|
||||||
|
weight: Double?,
|
||||||
|
selectedStorageList: List<com.lukouguoji.module_base.bean.GjcStorageUse>
|
||||||
|
) {
|
||||||
if (selectedStorageList.isEmpty()) {
|
if (selectedStorageList.isEmpty()) {
|
||||||
showToast("请选择要出库的库位")
|
showToast("请选择要出库的库位")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将选中的子列表项转换为RequestBody
|
// 构建请求参数:出库件数 + 出库重量 + 选中的子列表项
|
||||||
val params = selectedStorageList.toRequestBody()
|
val params = mapOf(
|
||||||
|
"pc" to pc,
|
||||||
|
"weight" to weight,
|
||||||
|
"storageUseList" to selectedStorageList
|
||||||
|
).toRequestBody()
|
||||||
|
|
||||||
launchLoadingCollect({ NetApply.api.outIntExpStorage(params) }) {
|
launchLoadingCollect({ NetApply.api.outIntExpStorage(params) }) {
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
@@ -305,11 +317,15 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
|
|||||||
* 执行入库操作
|
* 执行入库操作
|
||||||
* @param locationName 库位名称 (后端的location字段)
|
* @param locationName 库位名称 (后端的location字段)
|
||||||
* @param locationId 库位ID (后端的locationId字段)
|
* @param locationId 库位ID (后端的locationId字段)
|
||||||
|
* @param pc 入库件数
|
||||||
|
* @param weight 入库重量
|
||||||
* @param maWbListForInStorage 包含选中子列表项的主列表数据
|
* @param maWbListForInStorage 包含选中子列表项的主列表数据
|
||||||
*/
|
*/
|
||||||
fun performInStorage(
|
fun performInStorage(
|
||||||
locationName: String,
|
locationName: String,
|
||||||
locationId: String,
|
locationId: String,
|
||||||
|
pc: Long?,
|
||||||
|
weight: Double?,
|
||||||
maWbListForInStorage: List<GjcMaWb>
|
maWbListForInStorage: List<GjcMaWb>
|
||||||
) {
|
) {
|
||||||
if (maWbListForInStorage.isEmpty()) {
|
if (maWbListForInStorage.isEmpty()) {
|
||||||
@@ -322,10 +338,12 @@ class IntExpStorageUseViewModel : BasePageViewModel() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建请求参数:库位名称 + 库位ID + 完整的主子列表结构
|
// 构建请求参数:库位名称 + 库位ID + 入库件数 + 入库重量 + 完整的主子列表结构
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"location" to locationName,
|
"location" to locationName,
|
||||||
"locationId" to locationId.toLongOrNull(),
|
"locationId" to locationId.toLongOrNull(),
|
||||||
|
"pc" to pc,
|
||||||
|
"weight" to weight,
|
||||||
"maWbList" to maWbListForInStorage
|
"maWbList" to maWbListForInStorage
|
||||||
).toRequestBody()
|
).toRequestBody()
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import com.lukouguoji.module_base.common.ConstantEvent
|
|||||||
import com.lukouguoji.module_base.http.net.NetApply
|
import com.lukouguoji.module_base.http.net.NetApply
|
||||||
import com.lukouguoji.module_base.impl.FlowBus
|
import com.lukouguoji.module_base.impl.FlowBus
|
||||||
import com.lukouguoji.module_base.ktx.commonAdapter
|
import com.lukouguoji.module_base.ktx.commonAdapter
|
||||||
|
import com.lukouguoji.module_base.ktx.CustomsResponseUrl
|
||||||
|
import com.lukouguoji.module_base.ktx.ITEM_CLICK_TYPE_CUSTOMS_RESPONSE
|
||||||
|
import com.lukouguoji.module_base.ktx.showCustomsResponse
|
||||||
import com.lukouguoji.module_base.ktx.launchCollect
|
import com.lukouguoji.module_base.ktx.launchCollect
|
||||||
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
||||||
import com.lukouguoji.module_base.ktx.showToast
|
import com.lukouguoji.module_base.ktx.showToast
|
||||||
@@ -42,7 +45,8 @@ class IntExpTallyViewModel : BasePageViewModel() {
|
|||||||
val totalWeight = MutableLiveData("0") // 总重量
|
val totalWeight = MutableLiveData("0") // 总重量
|
||||||
|
|
||||||
// ========== 全选状态 ==========
|
// ========== 全选状态 ==========
|
||||||
val isAllChecked = MutableLiveData(false)
|
val isAllChecked = MutableLiveData(false) // 主单全选状态
|
||||||
|
val isAllHaWbChecked = MutableLiveData(false) // 分单全选状态(仅统计允许全选的主单下的分单)
|
||||||
|
|
||||||
fun onItemCheckChanged() {
|
fun onItemCheckChanged() {
|
||||||
updateCheckAllStatus()
|
updateCheckAllStatus()
|
||||||
@@ -51,6 +55,10 @@ class IntExpTallyViewModel : BasePageViewModel() {
|
|||||||
fun updateCheckAllStatus() {
|
fun updateCheckAllStatus() {
|
||||||
val list = pageModel.rv?.commonAdapter()?.items as? List<GjcMaWb> ?: return
|
val list = pageModel.rv?.commonAdapter()?.items as? List<GjcMaWb> ?: return
|
||||||
isAllChecked.value = list.isNotEmpty() && list.all { it.checked.get() }
|
isAllChecked.value = list.isNotEmpty() && list.all { it.checked.get() }
|
||||||
|
|
||||||
|
// 分单全选状态:只看允许全选的主单下的分单
|
||||||
|
val eligibleHaWbList = list.filter { it.canCheckAllHaWb }.flatMap { it.haWbList ?: emptyList() }
|
||||||
|
isAllHaWbChecked.value = eligibleHaWbList.isNotEmpty() && eligibleHaWbList.all { it.checked.get() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 全局展开状态 ==========
|
// ========== 全局展开状态 ==========
|
||||||
@@ -86,6 +94,43 @@ class IntExpTallyViewModel : BasePageViewModel() {
|
|||||||
pageModel.rv?.commonAdapter()?.notifyDataSetChanged()
|
pageModel.rv?.commonAdapter()?.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分单全选按钮点击 (只切换分单的选中状态,主单选择独立)
|
||||||
|
* 规则:主单存在分批申报(接口 allTally=0)时,不允许选中该主单下的分单;
|
||||||
|
* 只有主单全部申报完毕(allTally=1)才允许全选其分单。
|
||||||
|
*/
|
||||||
|
fun checkAllHaWbClick() {
|
||||||
|
val list = pageModel.rv?.commonAdapter()?.items as? List<GjcMaWb> ?: return
|
||||||
|
|
||||||
|
val shouldCheckAll = !isAllHaWbChecked.value!!
|
||||||
|
var eligibleCount = 0 // 允许全选的分单数
|
||||||
|
var skippedMaWbCount = 0 // 因未全部申报而跳过的主单数
|
||||||
|
|
||||||
|
list.forEach { maWb ->
|
||||||
|
val haWbList = maWb.haWbList
|
||||||
|
if (haWbList.isNullOrEmpty()) return@forEach
|
||||||
|
if (maWb.canCheckAllHaWb) {
|
||||||
|
eligibleCount += haWbList.size
|
||||||
|
haWbList.forEach { it.checked.set(shouldCheckAll) }
|
||||||
|
} else if (shouldCheckAll) {
|
||||||
|
// 主单未全部申报完毕,跳过其分单(保持原有选中状态不变)
|
||||||
|
skippedMaWbCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isAllHaWbChecked.value = shouldCheckAll && eligibleCount > 0
|
||||||
|
|
||||||
|
if (shouldCheckAll) {
|
||||||
|
if (eligibleCount == 0) {
|
||||||
|
showToast("暂无可全选的分单,主单需全部申报完毕后才可全选其分单")
|
||||||
|
} else if (skippedMaWbCount > 0) {
|
||||||
|
showToast("有${skippedMaWbCount}票主单未全部申报完毕,其分单未选中")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pageModel.rv?.commonAdapter()?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切换全局展开/收起状态
|
* 切换全局展开/收起状态
|
||||||
*/
|
*/
|
||||||
@@ -344,4 +389,14 @@ class IntExpTallyViewModel : BasePageViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表项点击分发:侧滑「回执」按钮 → 查询海关回执并跳转只读详情页
|
||||||
|
*/
|
||||||
|
override fun onItemClick(position: Int, type: Int) {
|
||||||
|
val bean = pageModel.rv?.commonAdapter()?.getItem(position) as? GjcMaWb ?: return
|
||||||
|
if (type == ITEM_CLICK_TYPE_CUSTOMS_RESPONSE) {
|
||||||
|
showCustomsResponse(CustomsResponseUrl.INT_EXP_TALLY, bean.prefix, bean.no, "出港理货回执")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,16 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<!-- 日期类型(入库/离港) -->
|
||||||
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0.7"
|
||||||
|
hint='@{"请选择日期类型"}'
|
||||||
|
list="@{viewModel.dateStateList}"
|
||||||
|
type="@{SearchLayoutType.SPINNER}"
|
||||||
|
value="@={viewModel.dateState}" />
|
||||||
|
|
||||||
<!-- 航班日期起 -->
|
<!-- 航班日期起 -->
|
||||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@@ -72,15 +82,15 @@
|
|||||||
type="@{SearchLayoutType.SPINNER}"
|
type="@{SearchLayoutType.SPINNER}"
|
||||||
value="@={viewModel.agentId}" />
|
value="@={viewModel.agentId}" />
|
||||||
|
|
||||||
<!-- 出库状态 -->
|
<!-- 航班号 -->
|
||||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
|
android:id="@+id/flight_no_search"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
hint='@{"请选择出库状态"}'
|
hint='@{"请输入航班号"}'
|
||||||
list="@{viewModel.outStatusList}"
|
type="@{SearchLayoutType.INPUT}"
|
||||||
type="@{SearchLayoutType.SPINNER}"
|
value="@={viewModel.flightNo}" />
|
||||||
value="@={viewModel.outStatus}" />
|
|
||||||
|
|
||||||
<!-- 运单号 -->
|
<!-- 运单号 -->
|
||||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
@@ -204,6 +214,16 @@
|
|||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="总重量:100" />
|
tools:text="总重量:100" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:text='@{`航班航次:` + viewModel.totalFlightNumber}'
|
||||||
|
android:textColor="@color/bottom_tool_tips_text_color"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="航班航次:2" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -356,7 +356,7 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 第7行:托盘车号、托盘车自重、业务类型 -->
|
<!-- 第7行:托盘数量、托盘自重(用于自动计算运抵重量)、业务类型 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -364,22 +364,23 @@
|
|||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
hint='@{"请输入托盘车号"}'
|
hint='@{"请输入托盘数量"}'
|
||||||
setRefreshCallBack="@{viewModel::onCarIdInputComplete}"
|
inputType="@{android.text.InputType.TYPE_CLASS_NUMBER}"
|
||||||
title='@{"托盘车号"}'
|
title='@{"托盘数量"}'
|
||||||
titleLength="@{5}"
|
titleLength="@{5}"
|
||||||
type="@{DataLayoutType.INPUT}"
|
type="@{DataLayoutType.INPUT}"
|
||||||
value='@={viewModel.maWbBean.carId}'
|
value='@={viewModel.trayNumber}'
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1" />
|
android:layout_weight="1" />
|
||||||
|
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
enable="@{false}"
|
hint='@{"请输入托盘自重"}'
|
||||||
title='@{"托盘车自重"}'
|
inputType="@{android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL}"
|
||||||
|
title='@{"托盘自重"}'
|
||||||
titleLength="@{5}"
|
titleLength="@{5}"
|
||||||
type="@{DataLayoutType.INPUT}"
|
type="@{DataLayoutType.INPUT}"
|
||||||
value='@{viewModel.carWeight}'
|
value='@={viewModel.trayWeight}'
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="15dp"
|
android:layout_marginStart="15dp"
|
||||||
@@ -399,17 +400,47 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 第8行:备注 -->
|
<!-- 第8行:平板车、平板车自重、备注 -->
|
||||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
<LinearLayout
|
||||||
hint='@{"请输入备注"}'
|
|
||||||
inputHeight="@{80}"
|
|
||||||
title='@{"备 注"}'
|
|
||||||
titleLength="@{5}"
|
|
||||||
type="@{DataLayoutType.INPUT}"
|
|
||||||
value='@={viewModel.pageRemark}'
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp" />
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
hint='@{"请输入平板车号"}'
|
||||||
|
setRefreshCallBack="@{viewModel::onCarIdInputComplete}"
|
||||||
|
title='@{"平 板 车"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@={viewModel.maWbBean.carId}'
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"平板车自重"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.carWeight}'
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="15dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
hint='@{"请输入备注"}'
|
||||||
|
title='@{"备 注"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@={viewModel.pageRemark}'
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="15dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,16 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1" />
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<!-- 运抵状态 -->
|
||||||
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
|
hint='@{"请选择运抵状态"}'
|
||||||
|
list="@{viewModel.arrivalStatusList}"
|
||||||
|
type="@{SearchLayoutType.SPINNER}"
|
||||||
|
value="@={viewModel.arrivalStatus}"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
<!-- 搜索按钮 -->
|
<!-- 搜索按钮 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|||||||
@@ -0,0 +1,259 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<import type="com.lukouguoji.module_base.ui.weight.data.layout.DataLayoutType" />
|
||||||
|
|
||||||
|
<import type="com.lukouguoji.module_base.common.DetailsPageType" />
|
||||||
|
|
||||||
|
<import type="android.view.View" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="viewModel"
|
||||||
|
type="com.lukouguoji.gjc.viewModel.IntExpMoveEditViewModel" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/color_f2"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include layout="@layout/title_tool_bar" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="15dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_white_radius_8"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="15dp">
|
||||||
|
|
||||||
|
<!-- 第一行:运单号、件数、重量(均只读) -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"运单号"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.wbNo}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"件数"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.pc == null ? "" : String.valueOf(viewModel.dataBean.pc)}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"重量"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.weight == null ? "" : String.valueOf(viewModel.dataBean.weight)}' />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 第二行:特码、代理人、品名(均只读) -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"特码"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.spCode}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"代理人"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.agentCode}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"品名"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.goods}' />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 第三行:出运路径、运单类型、承运人(均只读,字段名以 /IntExpMove/detail 文档为准) -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"出运路径"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.range}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"运单类型"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.awbTypeName}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"承运人"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.by}' />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 第四行:移交状态(只读)、备注(编辑模式可编辑) -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
enable="@{false}"
|
||||||
|
title='@{"移交状态"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@{viewModel.dataBean.moveStateText}' />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||||
|
hint='@{"请输入备注"}'
|
||||||
|
title='@{"备注"}'
|
||||||
|
titleLength="@{5}"
|
||||||
|
type="@{DataLayoutType.INPUT}"
|
||||||
|
value='@={viewModel.remark}' />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 交接图片区域:label 在左,图片在右 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="15dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:text="交接图片"
|
||||||
|
android:textColor="@color/text_gray"
|
||||||
|
completeSpace="@{5}" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rv_images"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:overScrollMode="never"
|
||||||
|
itemLayoutId="@{viewModel.imageItemLayoutId}"
|
||||||
|
viewHolder="@{viewModel.imageItemViewHolder}"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:spanCount="9" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 详情模式下无图片时的占位提示 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_no_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="暂无图片"
|
||||||
|
android:textColor="@color/text_gray"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:visibility="@{viewModel.showNoImage ? View.VISIBLE : View.GONE}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 底部按钮(详情模式不显示) -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginBottom="15dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="@{viewModel.pageType != DetailsPageType.Details ? View.VISIBLE : View.GONE}">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/tv_bottom_btn"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:onClick="@{()-> viewModel.cancel()}"
|
||||||
|
android:text="取消" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/tv_bottom_btn"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:onClick="@{()-> viewModel.submit()}"
|
||||||
|
android:text="保存" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
||||||
@@ -178,6 +178,12 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 允许装机按钮(多选) -->
|
||||||
|
<TextView
|
||||||
|
style="@style/tv_bottom_btn"
|
||||||
|
android:onClick="@{()-> viewModel.allowLoadClick()}"
|
||||||
|
android:text="允许装机" />
|
||||||
|
|
||||||
<!-- 待配运按钮 -->
|
<!-- 待配运按钮 -->
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/tv_bottom_btn"
|
style="@style/tv_bottom_btn"
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType"/>
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="model"
|
||||||
|
type="com.lukouguoji.gjc.dialog.IntExpArriveDeleteDialogModel" />
|
||||||
|
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="480dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_dialog_f2_radius_10"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:background="@drawable/bg_primary_radius_top_10"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="删除原因"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- 表单内容 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="30dp"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- 变更原因 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
completeSpace="@{6}"
|
||||||
|
android:text="变更原因:"
|
||||||
|
android:textColor="@color/text_normal"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
type="@{SearchLayoutType.SPINNER}"
|
||||||
|
list="@{model.changeReasonList}"
|
||||||
|
value="@={model.changeReason}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 联系人姓名 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
completeSpace="@{6}"
|
||||||
|
android:text="联系人姓名:"
|
||||||
|
android:textColor="@color/text_normal"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
hint='@{"请输入联系人姓名"}'
|
||||||
|
type="@{SearchLayoutType.INPUT}"
|
||||||
|
value="@={model.contactName}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 联系人电话 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
completeSpace="@{6}"
|
||||||
|
android:text="联系人电话:"
|
||||||
|
android:textColor="@color/text_normal"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
hint='@{"请输入联系人电话"}'
|
||||||
|
type="@{SearchLayoutType.INPUT}"
|
||||||
|
value="@={model.contactPhone}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:layout_marginBottom="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/tv_bottom_btn"
|
||||||
|
android:onClick="@{()->model.dismiss()}"
|
||||||
|
android:text="取消" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/tv_bottom_btn"
|
||||||
|
android:onClick="@{()->model.onConfirmClick()}"
|
||||||
|
android:text="确定" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
||||||
@@ -62,6 +62,60 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 件数 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="15dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
completeSpace="@{5}"
|
||||||
|
android:text="件数:"
|
||||||
|
android:textColor="@color/text_normal"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
|
android:id="@+id/pc_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
type="@{SearchLayoutType.INPUT}"
|
||||||
|
hint='@{"请输入件数"}'
|
||||||
|
value="@={model.pc}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 重量 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="15dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
completeSpace="@{5}"
|
||||||
|
android:text="重量:"
|
||||||
|
android:textColor="@color/text_normal"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
|
android:id="@+id/weight_input"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
type="@{SearchLayoutType.INPUT}"
|
||||||
|
hint='@{"请输入重量"}'
|
||||||
|
value="@={model.weight}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 底部按钮 -->
|
<!-- 底部按钮 -->
|
||||||
|
|||||||
@@ -61,6 +61,32 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 备注 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="15dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
completeSpace="@{5}"
|
||||||
|
android:text="备注:"
|
||||||
|
android:textColor="@color/text_normal"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
hint='@{"请输入备注"}'
|
||||||
|
type="@{SearchLayoutType.INPUT}"
|
||||||
|
value="@={model.clearRemark}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 底部按钮 -->
|
<!-- 底部按钮 -->
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user