Compare commits
12 Commits
9c4588e47e
...
feature/ph
| Author | SHA1 | Date | |
|---|---|---|---|
| 49f11ef8e6 | |||
| eb0655d3fc | |||
| a33776d959 | |||
| b2a2cce585 | |||
| 3b732c3aec | |||
| 33c9f0e030 | |||
| 436202f15b | |||
| ef8e7cfacb | |||
| 1eb288a703 | |||
| 5a019a55c4 | |||
| bbc29ca2e1 | |||
| db062c8ee3 |
@@ -1,14 +1,10 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"空港集团 - API 文档": {
|
||||
"command": "/Users/kid/.version-fox/sdks/nodejs/bin/npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"apifox-mcp-server@latest",
|
||||
"--project-id=7382863"
|
||||
],
|
||||
"env": {
|
||||
"APIFOX_ACCESS_TOKEN": "APS-S2aVVwqasbdByzPLgSqryRC8BB0ZFqhQ"
|
||||
"api-doc": {
|
||||
"type": "http",
|
||||
"url": "https://www.agentfoxapp.com/mcp/976caff7-5f98-4487-bccf-3aa20c92cf1f",
|
||||
"headers": {
|
||||
"Authorization": "Bearer afk_G_VKmHTdXUi3GjQdg5AeHeH-0BSYQEPP"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
307
.claude/skills/phone-adapt/SKILL.md
Normal file
307
.claude/skills/phone-adapt/SKILL.md
Normal file
@@ -0,0 +1,307 @@
|
||||
---
|
||||
name: phone-adapt
|
||||
description: 参照 documents/5.5寸手持端设计文件/ 的 HTML 设计稿,把 AirLogistics 指定页面适配成 5.5 寸手机竖屏版本——浏览器渲染设计稿逐态截图分析、复用 Phone* 公共组件写手机布局、复用现有 ViewModel 与接口、保证 Pad 端零改动,最后走完整登录流程实机联调并与设计稿逐项核对 UI。当用户说"适配手机端/做手机版/手持端适配"、点名某个设计稿入口(如"02 出库交接"、"做一下出港计重")、要求"按设计稿还原页面"、或提到 5.5 寸/手机版/双形态时,都要使用本 skill,即使没明说"适配"二字。
|
||||
---
|
||||
|
||||
# 5.5 寸手持端页面适配
|
||||
|
||||
把一个已有的平板页面,按设计稿适配出手机竖屏版本。核心约束是**只重写布局,业务逻辑与接口全部复用**,
|
||||
Pad 端行为必须零变化。
|
||||
|
||||
先读 `CLAUDE.md` 的「5.5 寸手持端(手机版)双形态适配规范」章节——那里是权威规范,本 skill 是执行流程。
|
||||
|
||||
## 前置确认
|
||||
|
||||
需要用户给出**目标页面**(设计稿目录名或业务名,如「02 出库交接」/「出港计重」)。
|
||||
其余自己查:`documents/5.5寸手持端设计文件/00_全部入口总览.html` 列出全部 11 个入口,
|
||||
每个子目录下可能有多个 HTML(主页 + 详情 + 子页面),都要处理。
|
||||
|
||||
对应的平板页面靠业务名在 `module_gjc/gjj/gnc/gnj` 里搜 Activity。**注意认准首页菜单实际跳转的那个
|
||||
Activity**,同名旧版文件在这个仓库里很常见。
|
||||
|
||||
## 阶段 1:渲染设计稿,看图不看代码
|
||||
|
||||
设计稿是带 Tailwind + JS 的交互原型,一个 HTML 里常塞了列表页、Tab、详情页、筛选弹层,靠 JS 切换。
|
||||
|
||||
**只读 HTML 源码会出错**,必须渲染成图:
|
||||
|
||||
```bash
|
||||
S=.claude/skills/phone-adapt/scripts
|
||||
# 1) 先看有哪些状态可切
|
||||
python3 $S/design_shots.py --html "documents/5.5寸手持端设计文件/02_出库交接/出库交接.html" --list
|
||||
|
||||
# 2) 按 --list 结果逐态渲染(照抄 onclick 里的真实参数最省事)
|
||||
python3 $S/design_shots.py --html "…/出库交接.html" --out /tmp/design \
|
||||
--state "01_列表:" \
|
||||
--state "02_已交接Tab:switchTab('shipped');" \
|
||||
--state "03_详情:showDetail({id:'PMC90901CZ',status:'已交接',rackNo:'076'});" \
|
||||
--state "04_筛选弹层:toggleFilterDrawer(true);"
|
||||
```
|
||||
|
||||
然后用 Read 工具**逐张看图**,抽取:配色、字号、圆角、间距、图标、控件顺序、空态、
|
||||
各状态下哪些元素显示/隐藏(例如「已交接」Tab 下底部操作条整条消失)。
|
||||
|
||||
`--list` 输出里如果出现 `window.location.href='xxx.html'`,说明是多文件原型,那些文件同样要渲染。
|
||||
|
||||
**设计稿的 JS 可能有 bug,别照抄字段映射。** 出库交接的详情页就把「航班日期」绑成了重量、
|
||||
「备注」绑成了航班号(HTML 里 id 重复)。判断依据是**静态 label 文案**:label 写「航班日期」
|
||||
就绑 `fdate`,写「板型」就绑 `boardType`。label 是设计意图,JS 只是 demo 接线。
|
||||
|
||||
标签文案同理:卡片上写死的「IMP代码」是占位符,实际要绑 `bean.dgrCode` 的值。
|
||||
|
||||
## 阶段 2:对齐接口能力,不要臆想
|
||||
|
||||
把设计稿的每个筛选项/Tab/字段,对照平板 ViewModel 已有的字段:
|
||||
|
||||
- **已有字段** → 直接复用(如出港移库的 Tab 直接绑既有 `moveState`)
|
||||
- **平板没有的新维度**(新 Tab 状态、新下拉、新排序)→ **先查 api-doc MCP 拿权威参数名**,
|
||||
流程见 `references/api-doc.md`(连接方式 + 5 个工具 + 逐接口核对清单)
|
||||
|
||||
**最高决策规则(用户 2026-07-29 拍板,此后不再逐次询问):业务改动完全以设计稿为准,不做妥协。**
|
||||
接口不支持设计稿的某个维度时:UI 照设计稿完整实现(绑预留字段/占位,传参后端静默忽略无副作用),
|
||||
缺口直接登记 `documents/后端接口对接问题清单.xlsx` 提修改需求,后端补齐后前端自动生效。
|
||||
仅当设计稿本身歧义(label 无法推断字段语义)才用 `AskUserQuestion` 问用户。
|
||||
|
||||
这一步不能猜,也**不要拿「实测不生效」当结论**。出库交接第一版猜了 `hoState`,实测两个 Tab
|
||||
返回同一批数据,一度误判为「后端不支持」;后查 api-doc 才发现正确参数名是 `handoverState`——
|
||||
后端对不认识的参数是**静默忽略**,不报错,实测只能证明「这个名字不对」,证不出「后端没这能力」。
|
||||
|
||||
「参数是否真的生效」的 A/B 验证优先走 Proxyman MCP(`create_compose_http_from_flow` 复制
|
||||
真实请求改参重发,token 自动带上),分工与流程见 `references/proxyman.md`:
|
||||
**api-doc 管契约(该有什么),Proxyman 管事实(实际发/回了什么)**。
|
||||
|
||||
核对完把结论落两处(模板与格式见 `references/api-doc.md`):
|
||||
|
||||
- 后端真实缺口(缺入参/缺出参/数据不回填)→ 记入 `documents/后端接口对接问题清单.xlsx`,
|
||||
这是与后端对接的正式文档,按「模块 → 页面 → 接口定义 → 接口地址」索引,**每个页面适配完都要维护**
|
||||
- 参数名/字段绑定错误这类前端问题 → 直接改代码
|
||||
|
||||
阶段 5 仍要用真实请求体验证新参数确实生效;没生效就在清单文档与交付说明里如实标注,
|
||||
不要让它看起来是通的。
|
||||
|
||||
### 开工前对齐方案
|
||||
|
||||
分析完先把结论摊给用户,避免闷头做错方向。一次说清,别挤牙膏:
|
||||
|
||||
```
|
||||
📱 页面适配方案:<页面名>
|
||||
|
||||
设计稿:documents/5.5寸手持端设计文件/<目录>/(N 个 HTML:主页 / 详情 / …)
|
||||
平板页面:XxxActivity + XxxViewModel(module_xxx)
|
||||
|
||||
手机版结构:
|
||||
搜索行:<字段>(含扫码/筛选按钮)
|
||||
状态 Tab:<Tab1> / <Tab2> → 绑定 <字段名>
|
||||
列表卡片:<字段…>,两种形态(<形态A> / <形态B>)
|
||||
底部操作条:全选 + 已选统计 + [<按钮>]
|
||||
筛选弹层:<字段1> / <字段2> / <字段3>
|
||||
二级页:<详情页名>(新增 Activity / 复用已有)
|
||||
|
||||
复用组件:PhoneSearchBar、PhoneStatusTab、PhoneStatBox…(无需新增 / 需新增 XXX)
|
||||
|
||||
接口:列表 <接口> 复用;新增参数 <名>(待你确认);新增字典 <DictUtils 方法>
|
||||
Pad 端影响:无(新字段默认空、额外请求由手机开关控制)
|
||||
|
||||
设计稿与平板的差异:<如手机版无「配运」按钮>
|
||||
|
||||
确认后开始编码。
|
||||
```
|
||||
|
||||
## 阶段 3:写布局,复用公共组件
|
||||
|
||||
`module_base/.../ui/weight/phone/` 已有 7 个组件,**优先复用,不要重写 XML**。
|
||||
组件清单、属性表与用法见 `references/components.md`(写布局前读一遍)。
|
||||
|
||||
改造步骤:
|
||||
|
||||
1. 平板布局 `git mv` 到 `res/layout-sw600dp/`(item 布局同理)
|
||||
2. 在 `res/layout/` 下建**同名**文件写手机布局
|
||||
3. Kotlin 侧 `layoutId()` / `itemLayoutId` **一律不动**,不做设备分支
|
||||
|
||||
三条硬约束(违反会编译失败或运行时崩溃,细节见 `references/pad-safety.md`):
|
||||
|
||||
| 约束 | 说明 |
|
||||
|------|------|
|
||||
| 两变体 `<variable>` 完全一致 | 手机变体新增 `activity` 变量时,平板变体也要补上(写注释说明平板不用它) |
|
||||
| 共用 id 双方都要有 | ViewHolder/Activity 引用的 id(`srl`/`rv`/`iv_icon` 等)两边都保留 |
|
||||
| 单侧独有 id 自动 `@Nullable` | 如手机独有的 `ll_detail`,ViewHolder 里必须判空调用 |
|
||||
|
||||
**一套 item 布局覆盖多种卡片形态**,不要为「待交接/已交接」建两个布局。按 bean 状态切
|
||||
visibility 即可(Bean 上加个只读计算属性如 `isHandovered` 让 XML 更干净;计算属性不参与 Gson
|
||||
序列化,安全)。
|
||||
|
||||
## 阶段 4:接业务,且不碰 Pad
|
||||
|
||||
手机端新增的查询维度用这个模式,能做到平板端请求次数与行为零变化:
|
||||
|
||||
```kotlin
|
||||
// ViewModel:新字段默认空值,getData() 里仅非空才进请求
|
||||
val handoverState = MutableLiveData("") // 平板布局无对应控件 → 恒为空 → 不进请求
|
||||
val filterParams = mapOf(
|
||||
/* 原有字段… */
|
||||
"handoverState" to handoverState.value?.ifEmpty { null }, // 参数名以 api-doc 为准
|
||||
)
|
||||
|
||||
// 手机专属的额外请求(Tab 角标计数、新下拉字典)用内部开关控制
|
||||
private var phoneExtrasEnabled = false
|
||||
fun initPhoneExtras() { phoneExtrasEnabled = true; handoverState.value = "0"; /* 加载字典 */ }
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// Activity:只有手机形态才开启,平板不调用
|
||||
if (DeviceUtil.isPhone()) viewModel.initPhoneExtras()
|
||||
```
|
||||
|
||||
设备判断只用于**数据默认值与额外请求**,**绝不用于选布局**(选布局靠资源限定符)。
|
||||
|
||||
纯 UI 交互(弹层显隐、Tab 切换)写在 Activity,用 `ObservableBoolean` 暴露给 XML,不下沉到 ViewModel。
|
||||
|
||||
设计稿若少了平板上的某个按钮(如出库交接手机版只有「交接」没有「配运」),按设计稿做,
|
||||
但要在交付说明里点出来这个功能在手机端不可达,让用户决定。
|
||||
|
||||
新增手机专属页面(详情页等)记得:`app/src/main/AndroidManifest.xml` 注册 +
|
||||
`app/src/debug/AndroidManifest.xml` 追加 `exported="true"` 便于直启调试。
|
||||
|
||||
注册时 `screenOrientation` **必须写 `unspecified`**:
|
||||
|
||||
```xml
|
||||
<activity android:name="…"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="false"
|
||||
android:screenOrientation="unspecified" />
|
||||
```
|
||||
|
||||
- 写死 `userLandscape` → 手机端「先横屏、约 1 秒后转竖屏」闪一下(方向在建窗口时就定了,代码再纠正已晚)
|
||||
- 用 `@integer/xxx` 资源引用 → **更糟**:系统解析 Manifest 不套用设备限定符,平板会取到默认(竖屏)值,
|
||||
先竖后横,且 AutoSize 随之选错基准导致 UI 放大约 1.6 倍
|
||||
|
||||
`unspecified` 让系统按设备当前物理方向建窗口,两端起始方向本就正确,
|
||||
`BaseActivity` 的形态锁定不会产生二次纠正。
|
||||
|
||||
### ⚠️ 别忘了加手机端首页入口
|
||||
|
||||
手机端首页是 `module_p` 的 `PDAEnterActivity`,「国际」Tab 的菜单在
|
||||
`module_p/.../ui/enter/gj/GjViewModel.kt`(国内在 `gn/GnViewModel.kt`)。
|
||||
**菜单是手写清单,不是接口下发**;权限串只做过滤(`authList.contains(bean.key)`)。
|
||||
|
||||
适配完页面不回来加一行,手机上就**根本没有入口**——出库交接第一版就漏了这步,
|
||||
只能靠 adb 直启才能进去。加法:
|
||||
|
||||
```kotlin
|
||||
ActionBean(
|
||||
"出港移库",
|
||||
R.drawable.gjc_yi_ku_icon, // 图标复用 Pad 同款,复制 PNG 到 module_p/res/drawable-xxhdpi/
|
||||
Constant.AuthName.GjcYiKuListActivity, // 权限串与 Pad 端菜单一致
|
||||
ARouterConstants.ACTIVITY_URL_INT_EXP_MOVE // 填了 route 就走通用跳转,无需 when 分支
|
||||
),
|
||||
```
|
||||
|
||||
权限串和路由**都以 Pad 端 `app/.../HomeFragment.kt` 里该权限对应的 `ARouter.build(...)` 为准**
|
||||
(同一业务常有新旧两个 Activity,Pad 菜单实际跳的才是要适配的那个)。
|
||||
`module_p` 只依赖 `module_base`,看不到业务模块,所以跨模块必须走 ARouter、图标必须复制一份。
|
||||
|
||||
菜单没出现时先分清是「没权限」还是「没加清单」:
|
||||
|
||||
```bash
|
||||
adb -s <serial> shell "run-as com.lukouguoji.aerologic \
|
||||
cat /data/data/com.lukouguoji.aerologic/shared_prefs/data.xml" | tr ',' '\n' | grep -oE "App[A-Za-z]+" | sort -u
|
||||
```
|
||||
|
||||
## 阶段 5:端到端实机验证
|
||||
|
||||
先构建,再双端跑。命令与排障细节见 `references/verification.md`。
|
||||
|
||||
```bash
|
||||
./gradlew assembleDebug 2>&1 | grep -E "^(BUILD|FAILURE)|error:"
|
||||
```
|
||||
|
||||
构建过后核对 DataBinding 变体是否如预期(单侧 id 应为 `@Nullable`):
|
||||
|
||||
```bash
|
||||
grep -B3 "llDetail" module_gjc/build/generated/data_binding_base_class_source_out/debug/out/\
|
||||
com/lukouguoji/gjc/databinding/ItemXxxBinding.java
|
||||
```
|
||||
|
||||
**手机端**(真实数据链路,从登录开始):
|
||||
|
||||
```bash
|
||||
P=.claude/skills/phone-adapt/scripts/ui_probe.sh
|
||||
adb -s <phone> install -r -d app/build/outputs/apk/debug/app-debug.apk
|
||||
# 走登录 → 底部「国际」Tab → 点新加的菜单项进入(顺便验证入口加对了)
|
||||
# 同名文字多处时 tap-text 会提示命中数,用第 4 个参数指定序号(如底部 Tab 的「国际」)
|
||||
# 若账号确实没有该权限,改用冷启动直启(见 verification.md)
|
||||
$P <phone> form # 应打印 PHONE(sw=411dp…)
|
||||
$P <phone> wait-text "待交接" # 点击前先等页面就绪:加载弹窗会吞掉点击
|
||||
$P <phone> tap-text "已交接" # 按文字点击,比手算坐标可靠
|
||||
$P <phone> texts # 用文字变化断言点击确实生效(如底部条应消失)
|
||||
$P <phone> shot /tmp/p1.png
|
||||
$P <phone> crash # 必须为 0
|
||||
```
|
||||
|
||||
核对请求/响应体优先用 Proxyman MCP(`filter_flows` + `get_flow_detail`,比 `req` 的
|
||||
logcat 抓包完整可靠;模拟器代理一行命令接入,见 `references/proxyman.md`);
|
||||
Proxyman 不可用时兜底 `$P <phone> req pageQuery`。
|
||||
|
||||
逐项走查:列表/卡片两种形态、详情页、筛选弹层(重置+确认)、Tab 切换、单选与全选联动、
|
||||
下拉真实取数、扫码入口。每步 `crash` 都要为 0。
|
||||
|
||||
点完**一定要断言效果**(`texts` 或截图),不要假设点中了——加载弹窗盖屏时 `input tap` 会被静默吞掉。
|
||||
|
||||
手机端还要确认方向没闪屏(冷启动采样 `mRotation` 应恒为 0),命令见 `references/verification.md`。
|
||||
|
||||
**Pad 端回归**(必做):同样安装到平板模拟器打开同一页面截图,与改造前逐项对比——
|
||||
搜索条数量、默认值、按钮、底部统计都应一致。改过 `module_base` 公共组件时这步尤其不能省。
|
||||
|
||||
## 阶段 6:与设计稿二次核对
|
||||
|
||||
把阶段 1 的设计稿截图和阶段 5 的实机截图放在一起,用 Read 工具对照看,逐项核对:
|
||||
|
||||
配色 / 字号 / 字重 / 圆角 / 内外间距 / 图标形状与大小 / 控件顺序 / 选中态 / 状态标签配色 /
|
||||
**空值兜底**(真实数据空字段很常见——出库交接就出现过「库位」为空时橙色高亮标签只剩一个孤零零的
|
||||
小色块,需要空值时不套标签底)。
|
||||
|
||||
服务端当天没数据时(列表空)先放宽条件重查;仍为空、或要覆盖真实数据凑不出的形态
|
||||
(空字段、超长文本、罕见状态标签),**首选 Proxyman Map Local 造 mock**——零代码改动、
|
||||
零重构建,见 `references/proxyman.md` 场景 3(2026-07-31 出港查询用它一次验完
|
||||
已离港/已入库/未入库三种卡片形态)。验证完 `delete_rule` + 清模拟器代理。
|
||||
|
||||
Proxyman 不可用时才退回老办法:Activity 里临时注入样例 Bean 截图比对:
|
||||
|
||||
```kotlin
|
||||
// import com.lukouguoji.module_base.ktx.commonAdapter
|
||||
binding.rv.postDelayed({
|
||||
binding.rv.commonAdapter()?.refresh(listOf(/* 覆盖每种卡片形态各一条 */))
|
||||
}, 2500) // 等首次请求结束,否则会被真实结果覆盖
|
||||
```
|
||||
|
||||
**核对完必须删掉**,删完重新构建并 grep 确认无残留。
|
||||
|
||||
## 收尾:文档与交付说明
|
||||
|
||||
代码之外还要落四处文档,否则下一个人会重复踩坑:
|
||||
|
||||
- 新增了公共组件/资源 → 更新 `CLAUDE.md`「手机版公共组件库」表 + `references/components.md`
|
||||
- 踩到新坑(崩溃、静默失效、环境问题)→ 写进 `CLAUDE.md`「常见编译错误速查」和本 skill 的
|
||||
对应 reference,把**定位手法**也写上,不只写结论
|
||||
- 接口有后端缺口(阶段 2 / 阶段 5 发现的)→ 维护 `documents/后端接口对接问题清单.xlsx`;
|
||||
已确认的字段语义追加到 `references/api-doc.md` 的「已核对过的语义」,别让下个页面再查一遍
|
||||
- `CHANGELOG.md` 追加条目(新增 / 改进 / 修复 / 验证 四段),记忆文件
|
||||
`phone-5.5inch-adaptation.md` 同步
|
||||
|
||||
向用户汇报时明确写出这四块:
|
||||
|
||||
1. 新增/修改的文件清单(布局、组件、Activity/ViewModel/Bean)
|
||||
2. Pad 端零破坏的具体做法 + 回归结论
|
||||
3. 复用与新增的公共组件
|
||||
4. **待确认事项**:后端未生效的参数、自己推断的业务规则(如 IMP 代码红/绿的判定)、
|
||||
设计稿与平板功能的差异。如实说,别含糊。
|
||||
|
||||
## 参考文件
|
||||
|
||||
- `references/api-doc.md` — api-doc MCP 连接与逐接口核对流程、后端缺口记录规范、已核对字段语义(阶段 2 前读)
|
||||
- `references/proxyman.md` — Proxyman MCP 抓包/直调 A/B/Map Local mock 的接入与四个场景(阶段 2、5、6 用;与 api-doc 的分工:契约 vs 事实)
|
||||
- `references/components.md` — 7 个手机版公共组件的属性表、用法与封装新组件的规矩(写布局前读)
|
||||
- `references/pad-safety.md` — 布局变体约束、Pad 零破坏模式、易崩点(写代码前读)
|
||||
- `references/verification.md` — 双端 adb 命令、登录与直启、已知环境坑(验证前读)
|
||||
- `scripts/design_shots.py` — 设计稿逐态渲染截图
|
||||
- `scripts/ui_probe.sh` — 实机取文字/按文字点击/等待/截图/抓请求体/查崩溃
|
||||
160
.claude/skills/phone-adapt/references/api-doc.md
Normal file
160
.claude/skills/phone-adapt/references/api-doc.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# api-doc MCP:接口定义核对流程(阶段 2 必读)
|
||||
|
||||
适配页面前,**所有用到的接口都要在 api-doc(AgentFox)里核对一遍入参/出参**,
|
||||
不要靠平板代码或猜测推断参数名——出库交接就因为猜了 `hoState`(实际是 `handoverState`)
|
||||
白白折腾了一轮实机排查。
|
||||
|
||||
## 连接方式
|
||||
|
||||
项目 `.mcp.json` 已配置 `api-doc` 远程 HTTP MCP。若会话内 MCP 工具可直接调用
|
||||
(`mcp__api-doc__*`),直接用;**若工具没有注入会话**(ToolSearch 搜不到),用 curl 走
|
||||
MCP 协议手工调用,三步:
|
||||
|
||||
```bash
|
||||
URL=$(python3 -c "import json;print(json.load(open('.mcp.json'))['mcpServers']['api-doc']['url'])")
|
||||
AUTH="Authorization: $(python3 -c "import json;print(json.load(open('.mcp.json'))['mcpServers']['api-doc']['headers']['Authorization'])")"
|
||||
|
||||
# 1) initialize:从响应头 mcp-session-id 取会话 id
|
||||
curl -s -D /tmp/mcp_headers.txt -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \
|
||||
-H "Accept: application/json, text/event-stream" \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-code","version":"1.0"}}}' > /dev/null
|
||||
SID=$(grep -i 'mcp-session-id' /tmp/mcp_headers.txt | tr -d '\r' | awk '{print $2}')
|
||||
|
||||
# 2) initialized 通知(必须发,否则 tools/* 报 session 未初始化)
|
||||
curl -s -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \
|
||||
-H "Accept: application/json, text/event-stream" -H "mcp-session-id: $SID" \
|
||||
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}' > /dev/null
|
||||
|
||||
# 3) tools/call(响应是 SSE,`sed 's/^data: //'` 后按 JSON 解析)
|
||||
curl -s -X POST "$URL" -H "$AUTH" -H "Content-Type: application/json" \
|
||||
-H "Accept: application/json, text/event-stream" -H "mcp-session-id: $SID" \
|
||||
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_endpoints","arguments":{"keyword":"IntExpMove"}}}'
|
||||
```
|
||||
|
||||
## 可用工具(5 个)
|
||||
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `get_project_overview` | 项目概览 + 模块清单(首次调用) |
|
||||
| `list_modules` | 全部模块含描述 |
|
||||
| `list_endpoints` | 单模块内接口列表(分页,`moduleId` + `cursor`) |
|
||||
| `search_endpoints` | 按关键字搜接口(`keyword` 用控制器前缀最准,如 `IntExpOutHandover`) |
|
||||
| `get_endpoint_detail` | **核心**:单接口完整契约,schema 已拍平成字段表(`endpointId`) |
|
||||
|
||||
## 核对清单(每个页面做一遍)
|
||||
|
||||
对 ViewModel 里每个 `NetApply.api.xxx` 调用:
|
||||
|
||||
1. 从 `module_base/.../http/net/Api.kt` 拿到 `@POST` 路径 → `search_endpoints` 找到接口 → `get_endpoint_detail`
|
||||
2. **入参**:前端传的每个 key 必须能在文档字段表里找到**同名字段**;找不到 = 传了也白传(后端静默忽略,不报错)
|
||||
3. **类型**:状态类字段(`moveState`/`handoverState`/`outState`)文档均为 integer,前端传 `"0"` 字符串靠
|
||||
Jackson 隐式转换(实测可用);新代码尽量传数字
|
||||
4. **出参**:设计稿卡片/详情页的每个展示字段,都要能对上出参字段;对不上就是后端缺口
|
||||
5. **结论落两处**:
|
||||
- 后端缺口(缺入参/缺出参/数据不回填)→ 记入 `documents/后端接口对接问题清单.xlsx`
|
||||
(按「模块 → 页面 → 接口定义 → 接口地址」格式,含前端现状与期望,状态标 ⏳/🔍)
|
||||
- 前端可自行解决的(参数名写错、字段绑错)→ 直接改代码,别记成后端问题
|
||||
|
||||
## 已核对过的语义(别再猜)
|
||||
|
||||
- `IntExpOutHandover`:交接状态入参 = `handoverState`(不是 `hoState`),**2026-07-29 内网实测过滤生效**
|
||||
(传 0/1 分别返回纯净的待/已交接集合,字符串 `"0"`/`"1"` 同样被接受);`ldId` 是**组装人**不是交接人;
|
||||
交接人姓名出参 `hoUserName`,编码 `hoId`;查询入参**没有**交接人筛选(后端缺口 #4)
|
||||
- `IntExpOutHandover/pageQueryTotal`:**出参恒 0**(任何条件下 totalPc/totalWeight/wbNumber 均为 0,
|
||||
与同条件 pageQuery 不一致,后端缺口 #6)——依赖它的 Tab 角标/底部统计显示 0 不是前端问题
|
||||
- `IntExpMove`:出参无航班字段(缺口 #1)、无操作人姓名(缺口 #2);`opId`/`opdate`/`businessType`
|
||||
文档有定义但实测恒 null(缺口 #3);`pageQueryTotal` 正常(与 pageQuery 口径一致,`moveState` 过滤生效)
|
||||
- `typeCode/getSpecialCodeByParam`:`flag=1` 国际,`ieFlag` 必须传空串(传 `"E"` 返回空数组)
|
||||
- `eqm/uld`(ULD信息,6 个端点):查询入参仅 `uld`/`status`/`uldSuffix`(uldSuffix 名义是
|
||||
"uld后两位",实际就是**按所属航司筛选**,配 `DictUtils.getIntCarrierList`);`status` 仅两态
|
||||
0 正常/1 故障(三态需求=缺口 #7);无「来源」字段(#8)、无 `checkInDate`/`ifNo` 筛选入参(#9)、
|
||||
无批量删除(#10,`deleteUld` 是 `@Query` 单条);出参 `ifNo`/`efNo`/`checkInDate`/`checkOutDate`
|
||||
文档无描述,按命名推断进/出港航班号与出入库时间(#11 待确认)
|
||||
- `IntExpArrive`(出港运抵,2026-07-30 内网实测):`declareStatus`(申报状态)只在出参,
|
||||
`pageQuery`/`pageQueryTotal` 入参均无此字段(缺口 #20,待运抵/已运抵 Tab 靠客户端拆分);
|
||||
`declareStatus`/`GjcHaWb.arrivalStatus` 三色编码:`01`=正常(绿)、`W`=靛蓝态、其余非空=琥珀异常态;
|
||||
`haWbList[].response`/`arrivalOpDate` 是回执文本与对应时间,实测确认真实存在非空数据;
|
||||
分单补充收发货人信息**后端完全没有对应接口**(`search_endpoints` 对 `IntExpArrive`/`HaWb`
|
||||
关键字均搜不到分单更新接口,只有国际进港的 `IntImpAirManifest/complete`),
|
||||
实机调用拟定路径 `IntExpArrive/haWb/supplement` 确认 404(缺口 #19)
|
||||
- `IntExpSearch`(出港查询,2026-07-31 内网实测):查询入参 `outState`/`fno`/`dest`/`spCode`/`awbType`/
|
||||
`businessType`/`goodsCn`/`beginDate`/`endDate`/`agentCode`/`wbNo` 全部真实生效(平板既有口径);
|
||||
文档定义的 `isFclose`(筛选航班是否关闭)**pageQuery 与 pageQueryTotal 均静默忽略**(A/B 对照
|
||||
全量 72 票与单日两组,加参前后结果完全一致,缺口 #21);`pageQueryTotal` 出参无已入库/已离港
|
||||
分项计数(#21);`detail` 出参 Map 无 `lockStatus`(锁定状态)与计费重量字段(#22);
|
||||
详情「入库件数/入库重量」= `warehouseList` 各批次 pc/weight 求和(mock 与实测均吻合);
|
||||
卡片状态推断口径:`fclose` 非空=已离港、否则 `opDate` 非空=已入库;
|
||||
特码字典沿用 `DictUtils.getSpecialCodeList(flag=1, ieFlag="")`(ieFlag 必须空串)
|
||||
- `IntExpStorageUse`(出港仓库,2026-07-31 文档核对,内网 A/B 未测——当日开发机不在内网):
|
||||
`pageQuery`/`pageQueryTotal` 同一套入参 schema:`fdate`/`fno`/`wbNo`/`likeNo`/`spCode`/`dest`/
|
||||
`agentCode`/`location`/`clearNormal`/`checkIn`/`reviewStatus`/`dep`/`hno`/`prefix`;设计稿筛选的
|
||||
**运单类型/业务类型/品名(中) 均无入参**(`awbType`/`businessType`/`goodsCn`,缺口 #23);
|
||||
手机三格统计按 `clearNormal=0/1` 各调一次 `pageQueryTotal` 取 `wbNumber`,该过滤**是否真实生效
|
||||
待内网 A/B**(缺口 #24,参照 isFclose 有定义但被忽略的先例);出参 `clearNormal` 0=未清仓/1=已清仓
|
||||
(卡片标签与「清仓」按钮置灰按 ==\"1\" 判定);详情复用 `IntExpSearch/detail`(maWbId Query 参数)
|
||||
- `IntImpStorage`(进港仓库,2026-07-31 文档核对;**注意前端 Api.kt 路径前缀是 `IntImpStorage`
|
||||
不是 `IntImpStorageUse`**,api-doc 按后者搜不到):`pageQuery`/`pageQueryTotal` 同一套入参:
|
||||
`fdate`/`fno`/`wbNo`/`hno`/`fdep`(始发站)/`location`/`clearNormal`/`fid`;比出港仓库还少
|
||||
`agentCode`/`spCode`/`dest`——设计稿筛选的代理/特码/运单类型/业务类型/品名(中) **5 项均无入参**
|
||||
(缺口 #25);分项统计与 clearNormal 过滤待实测同 #24(记为 #26);详情复用
|
||||
`IntImpSearch/detail`(Body 传 `prefix`+`no`,出参 `data.maWb` 无 schema,字段 key 以平板绑定为准:
|
||||
`awbPc`/`awbWeight`/`cashWeight`/`inPc`/`inWeight`/`lockState`(0/1 需转中文)/`mftStatus` 原始舱单/
|
||||
`tallyStatus` 理货报告/`command` 海关放行/`by1` 承运人/`dlvTime` 出库时间;`dep`/`dest1`/`dest`/
|
||||
`unNumber`/`opDate` 为按出港推断,待实测 #27)
|
||||
- `IntImpPickUpDlv`(提取出库,2026-07-31 文档核对):`pageQuery` 入参含 **`outState`
|
||||
(0 未出库/1 已出库)**——待出库/已出库 Tab 直接真实过滤(抓包证实携带,效果待内网 A/B #29);
|
||||
另有 `fno`/`dest`/`origin`/`carrier`/`awbType`/`businessType`/`goods`/`likeNo`/`pkId`/
|
||||
`beginDate`/`endDate`+`dateType`(0 入库/1 离港)/`chargeFlag`(0 未提取/1 已提取);
|
||||
**无 `fdate`(航班日期)入参**(缺口 #28);出参无车牌/目的港/业务类型名称(#28);
|
||||
运单号筛选参数名是 **`no`**(平板既有口径);`chargeName`/`chargeTime` = 柜台办理人/缴费(提取)时间,
|
||||
`dlvTime` = 出库时间,`dlvName` 按提货人语义推断作「出库人」展示(待确认 #29);
|
||||
确认出库接口 `pickUpOut` 文档注明**需将全字段传至后端**(平板 `selectedItems.toRequestBody()` 已满足)
|
||||
- `IntImpSearch`(进港查询,2026-07-31 文档核对):`pageQuery`/`pageQueryTotal` 分页参数名是
|
||||
**`page`/`limit`**(不是 pageNum/pageSize);手机筛选 9 项入参全部真实存在:`beginDate`/`endDate`/
|
||||
`fno`/`agentCode`/`spCode`/`origin`(始发站)/`awbType`/`businessType`/`goods`(品名,**不是 goodsCn**);
|
||||
`outState`(0 未出库/1 已出库)文档为 integer,前端传字符串靠 Jackson 隐式转换;`pageQueryTotal`
|
||||
出参无已入库/已出库分项计数(缺口 #30,手机三格统计为客户端计数口径:dlvTime 非空=已出库、
|
||||
inDate 非空=已入库);出参含 `agentName`(中文名)+ `agentCode`,agentName 可为 null
|
||||
- 「进港移库」(设计稿 11)**归属更正(2026-08-03 用户确认):属国内进港移库 `DomImpMove`**,
|
||||
不是国际进港移库——「后端模块缺失」的旧结论(原缺口 #31)作废,原按 IntExpMove 约定预接的
|
||||
IntImpMove 前端实现已整体删除。溯源:api-doc 53 模块中移库仅 `DomImpMove`(国内进港,8 接口)与
|
||||
`IntExpMove`(国际出港,4 接口),移库按转关业务前缀划分(DomImpMove 处理 `CI**`、IntExpMove
|
||||
处理 `IO**`),设计稿详情卡的「国际进港」红标签实为 `awbType` 值(如「国际进港(国内中转)」),
|
||||
不代表页面归属
|
||||
- `DomImpMove`(国内进港移库,2026-08-03 内网实测):手机 Tab 双端点——待移库 `search` /
|
||||
已移库 `searchMoved`(同一套入参 schema,分页参数名 **`page`/`limit`**);入参 A/B 实测:
|
||||
**`wbNo`、`awbType`(值为 CICO/CIII/CIIO 转关码)过滤生效;`fdate`/`fno`/`spCode` 文档有定义但
|
||||
被静默忽略**(缺口 #32);`searchMoved`/`detail` 出参 **`opId`/`opDate` 恒 null** 且无操作人姓名
|
||||
字段(缺口 #33,移库人/移库时间显示"-"),`moveId` 形如 `CICO240617155016` 疑似内嵌移库时间戳但无
|
||||
文档语义;`searchMoved` 出参**无 pic 三字段**(search 有)——拍照上传页统一先调 `GET detail`
|
||||
回填已有照片(#34 前端已规避);照片提交走 `modify` 最小体
|
||||
`{mawbId, remark, picNumber, pic, originalPic}`(平板移交编辑页同链路,removeEmptyOrNull);
|
||||
批量移库 `move` 入参 `{fid:"0", ids:[mawbId]}`;国内进港特码字典
|
||||
`DictUtils.getSpecialCodeList(flag=0, ieFlag="I")` 实测取数正常
|
||||
- `IntExpCheckIn`(出港计重,2026-07-29 内网实测):`likeNo` **并非任意模糊**——按 8 位 `no`
|
||||
匹配(11 位 wbNo 或部分号均 0 条),11 位全号必须走 `wbNo`;`checkIn`/`checkInList`
|
||||
文档有定义但 `pageQuery`/`pageQueryTotal` **均静默忽略**(缺口 #12,待计重/计重中计数依赖);
|
||||
查询入参无通道号/承运人/运单类型(#13)、`checked/*` 无收运时间范围(#14);`checked/pageQuery`
|
||||
出参无通道号(#15)但有 `userName`(计重人中文名);`listRecordByWh` 出参计重人仅 `opId`(#16);
|
||||
`preArrive` 成功后 `pageQuery` 的 `arriveFlag` 不回填(#17);`splitCheckIn`/`completeCheckIn`
|
||||
无 `palletNumber`/`carWeight` 入参(#18,手机版托盘数量/自重按设计稿传预留参数);
|
||||
运单类型字典:`DictUtils.getWaybillTypeList2(ieFlag="E", type="IO")`
|
||||
|
||||
## 内网直调验证技巧(比驱动 UI 快得多)
|
||||
|
||||
登录态 token 存在 SharedPreference 里,取出后可以直接 curl 后端做行为验证
|
||||
(**注意:不要把 token 打印到输出里**,落临时文件引用):
|
||||
|
||||
```bash
|
||||
adb -s <serial> shell "run-as com.lukouguoji.aerologic cat \
|
||||
/data/data/com.lukouguoji.aerologic/shared_prefs/data.xml" \
|
||||
| grep -o '<string name="token">[^<]*' | sed 's/.*>//' > /tmp/air_token
|
||||
|
||||
curl -s -X POST "http://192.168.1.250:8093/IntExpOutHandover/pageQuery" \
|
||||
-H "Authorization: $(cat /tmp/air_token)" -H "Content-Type: application/json" \
|
||||
-d '{"pageNum":1,"pageSize":50,"handoverState":0}'
|
||||
```
|
||||
|
||||
用它做「参数是否生效」的 A/B 对比(同条件只改一个参数看 total 变化),
|
||||
再回 App UI 走一遍确认端到端链路。UI 里的滚轮日期选择器可用
|
||||
`input swipe`(慢速 500ms、每次 3 格 ≈ 396px)驱动,滑完截图校准。
|
||||
140
.claude/skills/phone-adapt/references/components.md
Normal file
140
.claude/skills/phone-adapt/references/components.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# 手机版公共组件(`module_base/.../ui/weight/phone/`)
|
||||
|
||||
7 个组件覆盖设计稿反复出现的部件。**先复用,实在没有再新增**——每个新组件都要维护、都要踩一遍
|
||||
状态恢复的坑,能少一个是一个。
|
||||
|
||||
所有属性都是**无命名空间写法**(与平板端 `PadSearchLayout` 一致),DataBinding 适配器集中在
|
||||
`PhoneWidgetKtx.kt`。同名属性(`hint`/`value`/`title`/`list`)按控件类型解析,与 Pad 系组件不冲突。
|
||||
|
||||
## 组件与属性
|
||||
|
||||
| 组件 | 用途 | 属性 |
|
||||
|------|------|------|
|
||||
| `PhoneSearchBar` | 顶部搜索行:搜索框 + 扫码 + 圆形筛选按钮 | `hint`、`value`(双向)、`showScan`、`showFilter`、`upperCase`、`setOnScanClickListener`、`setOnFilterClickListener`、`setRefreshCallBack` |
|
||||
| `PhoneStatusTab` | 状态 Tab 条(文字 + 角标 + 选中下划线) | `tabs`(List\<KeyValue\>)、`counts`(List\<String\>)、`value`(双向)、`setOnTabChanged` |
|
||||
| `PhoneDataLayout` | 表单/筛选项,三形态 | `title`、`hint`、`type`(`PhoneDataLayoutType.INPUT`/`DATE`/`SPINNER`)、`value`(双向)、`list`、`required`、`enable`、`setRefreshCallBack` |
|
||||
| `PhoneFilterPanel` | 底部筛选弹层(遮罩 + 面板 + 重置/确认) | `title`、`panelVisible`、`setOnResetClick`、`setOnConfirmClick`、`setOnDismissClick` |
|
||||
| `PhoneBottomBar` | 底部操作条(全选 + 已选统计 + 主按钮) | `allChecked`、`countText`、`actionText`、`setOnAllCheckClick`、`setOnActionClick` |
|
||||
| `PhoneStatBox` | 卡片内 2~4 列统计框,未传 label 的列自动隐藏 | `label1..4`、`value1..4` |
|
||||
| `PhoneKvItem` | 详情页字段项(灰标签 + 数值) | `title`、`value`、`tag`(true → 高亮标签)、`tagColor`("orange" 默认 / "green" 放行正常态);占位符 "-"/"--" 不套标签底 |
|
||||
| `PhoneFormRow` | 行式表单项(左标签 minWidth 110dp + 右值 + 1px 分隔线),覆盖新增/修改/详情三态表单 | `title`、`hint`、`type`(`PhoneFormRowType.INPUT`/`SELECT`/`DATE`/`TEXT`)、`value`(双向)、`list`(SELECT 选项)、`required`、`enable`、`numeric`、`setRefreshCallBack` |
|
||||
|
||||
`PhoneFormRow` 要点:SELECT 内置 XPopup 底部选择面板(点击整行弹出,无需页面代码);TEXT 为只读右对齐、
|
||||
空值自动占位 "--"(详情态);type 用三元按 `pageType` 切换即可一套布局覆盖三态页面(参考
|
||||
`app/res/layout/activity_uld_edit.xml`)。`PhoneBottomBar` 的 `actionDanger="@{true}"` 切浅红删除按钮。
|
||||
|
||||
`PhoneFilterPanel` 内容插槽已内置 ScrollView(2026-07-31 加):筛选项少时行为不变;
|
||||
筛选项多到超屏(出港查询 9 项)时内容区收缩滚动,底部「重置/确认」按钮始终可见——
|
||||
此前无滚动时按钮会被挤出屏幕外且毫无报错,只表现为"点不到确认"。
|
||||
|
||||
⚠️ 两个已踩坑:
|
||||
- **FAB 盖住弹层**:浮动按钮带 elevation 时会浮在 `PhoneFilterPanel` 之上,弹层实例要加
|
||||
`android:elevation="8dp"`(> FAB 的 6dp)
|
||||
- **Spinner 选择回调是异步的**:`onItemSelected` 由系统 post,自动化测试里 tap 选项后立即 tap「确认」
|
||||
可能抢在回值之前发出请求(表现为 UI 已显示选中但请求参数为 null)——脚本里选完要 `sleep 1` 以上;
|
||||
真人操作间隔足够,不受影响
|
||||
|
||||
`KeyValue` 是 `dev.utils.app.info.KeyValue`,字段是 **`key`(显示文案)+ `value`(业务值)**,
|
||||
不是 `name`。`PhoneStatusTab` 的 `tabs` 就按 `KeyValue("待交接","0")` 传。
|
||||
|
||||
## PhoneFilterPanel 的内容插槽
|
||||
|
||||
筛选项直接写在标签内,组件 `onFinishInflate` 会自动把它们移进面板中部并施加 20dp 项间距,
|
||||
使用方不用关心内部层级、也不用逐个写 `marginTop`:
|
||||
|
||||
```xml
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
title='@{"筛选条件"}'
|
||||
panelVisible="@{activity.filterPanelVisible}"
|
||||
setOnResetClick="@{(v)-> activity.resetFilter()}"
|
||||
setOnConfirmClick="@{(v)-> activity.confirmFilter()}"
|
||||
setOnDismissClick="@{(v)-> activity.toggleFilterPanel()}">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
title='@{"航班日期"}' hint='@{"请选择航班日期"}'
|
||||
type="@{PhoneDataLayoutType.DATE}" value="@={viewModel.flightDate}" />
|
||||
|
||||
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
|
||||
```
|
||||
|
||||
记得在 `<data>` 里 `<import type="com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayoutType" />`。
|
||||
|
||||
## ⚠️ 新增复合组件必做:屏蔽子控件状态恢复
|
||||
|
||||
同一页面放多个同类组件时(比如筛选弹层里 3 个 `PhoneDataLayout`),它们的内部子控件
|
||||
(`et`/`tv`/`spinner`…)**共用同一套 id**。系统按 id 保存/恢复视图状态会跨实例串档。
|
||||
|
||||
实际后果(出库交接踩过):DATE 项的默认日期被邻近输入框恢复的空文本覆盖,
|
||||
`EditText.setText("")` 触发 `doOnTextChanged`,再经双向绑定把 ViewModel 的航班日期也写成空——
|
||||
**筛选条件静默丢失,界面上只表现为"日期没了",极难联想到状态恢复**。
|
||||
|
||||
所以每个含内部 id 的组件都要写:
|
||||
|
||||
```kotlin
|
||||
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) =
|
||||
dispatchFreezeSelfOnly(container)
|
||||
|
||||
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) =
|
||||
dispatchThawSelfOnly(container)
|
||||
```
|
||||
|
||||
取值本来就由 ViewModel + DataBinding 持有,不需要视图级恢复,屏蔽掉没有副作用。
|
||||
|
||||
配套的第二道防线:多形态组件里,文本回写要限定形态,别让隐藏控件影响取值:
|
||||
|
||||
```kotlin
|
||||
et.doOnTextChanged { text, _, _, _ ->
|
||||
if (type == PhoneDataLayoutType.INPUT) value = text.toString()
|
||||
}
|
||||
```
|
||||
|
||||
### 遇到"值被神秘清空"怎么定位
|
||||
|
||||
在 setter 里打一条带堆栈的日志,一次就能看到真凶:
|
||||
|
||||
```kotlin
|
||||
android.util.Log.d("DBG", "value '$field' -> '$v'", Throwable("trace"))
|
||||
```
|
||||
|
||||
出库交接那次的栈是 `TextView.onRestoreInstanceState → EditText.setText → doOnTextChanged`,
|
||||
指向非常明确。**排完记得删日志。**
|
||||
|
||||
## 新增组件的写法约定
|
||||
|
||||
照现有 7 个组件的模式来,保持一致:
|
||||
|
||||
1. 继承 `LinearLayout`/`FrameLayout`,`init` 里 `inflate(context, R.layout.layout_phone_xxx, this)`
|
||||
2. 内部布局用 `<merge tools:parentTag="android.widget.LinearLayout">`,避免多一层嵌套
|
||||
3. 属性做成 Kotlin `var` + setter 里立即生效;双向绑定值用
|
||||
`onChangeListener: InverseBindingListener?`
|
||||
4. BindingAdapter 统一加到 `PhoneWidgetKtx.kt`,函数名带组件名前缀避免顶层函数重名
|
||||
5. 有互斥形态时,**`type` 要先于 `value` 生效**(把 type 放进同一个多属性 adapter 的第一个
|
||||
`?.let`),否则 value 会写进错误的子控件
|
||||
6. 加上上面那两个 `dispatch*SelfOnly` 覆写
|
||||
7. 完成后同步更新 `CLAUDE.md` 的组件表和记忆文件 `phone-5.5inch-adaptation.md`
|
||||
|
||||
## 资源
|
||||
|
||||
配色一律用 `phone_*`,背景用 `bg_phone_*`,图标用 `ic_phone_*`,禁止写死色值。
|
||||
已有的(不够再加,加完更新 CLAUDE.md):
|
||||
|
||||
- 色:`phone_primary` `phone_page_bg` `phone_bg` `phone_stat_bg` `phone_text_title/body/hint/label/strong/weak_btn`
|
||||
`phone_divider` `phone_badge_bg` `phone_green` `phone_check_border` `phone_tag_gray_*` `phone_tag_green_*`
|
||||
`phone_tag_red_*` `phone_orange_bg/text`
|
||||
- 背景:`bg_phone_card` `bg_phone_search` `bg_phone_input` `bg_phone_stat_box` `bg_phone_panel_top`
|
||||
`bg_phone_icon_round` `bg_phone_btn_primary` `bg_phone_btn_outline` `bg_phone_btn_weak`(浅蓝弱按钮)
|
||||
`bg_phone_badge` `bg_phone_tag_green/gray/red/orange` `bg_phone_card_header_blue/green`
|
||||
`bg_phone_card_top_accent`(卡片顶部主色描边条,出港计重头部卡片)
|
||||
- 图标:`ic_phone_airplane` `ic_phone_location` `ic_phone_person` `ic_phone_check_circle`
|
||||
`ic_phone_chevron_right/down` `ic_phone_calendar` `ic_phone_close`
|
||||
`ic_phone_check_round_checked/unchecked`(圆形复选框)
|
||||
`ic_phone_clipboard`(剪贴板,白色 FAB 用)`ic_phone_weight`(砝码,可 tint)
|
||||
`ic_phone_package`(件数)`ic_phone_tag`(特码/标签)
|
||||
- 圆点:`bg_phone_dot_green/blue/gray`(状态指示点;蓝=出港查询卡片头,灰=时间线未达节点)
|
||||
- 复用 Pad 的矢量图:`img_search` `img_scan` `img_filter`(可 `app:tint` 染色)
|
||||
|
||||
设计稿里的 MDI 图标若缺失,直接写 24×24 的 vector(用 MDI 官方 path),比找位图靠谱且能染色。
|
||||
267
.claude/skills/phone-adapt/references/pad-safety.md
Normal file
267
.claude/skills/phone-adapt/references/pad-safety.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# Pad 端零破坏 & 易崩点
|
||||
|
||||
平板端是在跑的生产功能,适配手机不能改变它的任何行为。以下是必须遵守的约束和踩过的坑。
|
||||
|
||||
## 布局变体的三条硬约束
|
||||
|
||||
同名布局 + 资源限定符(`layout/` 手机 / `layout-sw600dp/` 平板)会让 DataBinding 生成
|
||||
**一个 Binding 基类 + 两个变体实现**,因此 Kotlin 侧零改动。代价是两个变体必须兼容:
|
||||
|
||||
### 1. `<variable>` 必须完全一致
|
||||
|
||||
手机变体需要 `activity` 变量(暴露纯 UI 方法给 XML)时,**平板变体也要补上**,否则编译报
|
||||
`variable not found`。平板不用它,加个注释说明即可:
|
||||
|
||||
```xml
|
||||
<!-- 平板端不使用,仅为与手机变体保持变量一致 -->
|
||||
<variable name="activity" type="com.lukouguoji.gjc.activity.XxxActivity" />
|
||||
```
|
||||
|
||||
### 2. 共用 id 双方都要保留
|
||||
|
||||
Activity / ViewHolder 引用到的 id(`srl`、`rv`、`iv_icon`、`checkIcon`…)两个变体都要有。
|
||||
可以换位置换样式,但 id 得在。
|
||||
|
||||
### 3. 单侧独有的 id 会变 `@Nullable`
|
||||
|
||||
只在手机变体存在的 id(如「查看详情」的 `ll_detail`),Binding 基类里是可空字段,
|
||||
Kotlin 侧必须判空:
|
||||
|
||||
```kotlin
|
||||
// 平板变体没有这个控件,为 null 时直接跳过
|
||||
binding.llDetail?.setOnClickListener { XxxDetailActivity.start(itemView.context, bean) }
|
||||
```
|
||||
|
||||
编译器会强制你处理,不用担心漏掉。改完可以核对生成代码确认:
|
||||
|
||||
```bash
|
||||
grep -B3 "llDetail" module_gjc/build/generated/data_binding_base_class_source_out/debug/out/\
|
||||
com/lukouguoji/gjc/databinding/ItemXxxBinding.java
|
||||
```
|
||||
|
||||
### ⛔ 绝对不要用「不同文件名 + 代码判断」切布局
|
||||
|
||||
`activity_xxx_phone.xml` 会生成 `ActivityXxxPhoneBinding`,而字段声明的类型是
|
||||
`ActivityXxxBinding`。泛型擦除让**编译期查不出来**,一进页面就 `ClassCastException`。
|
||||
|
||||
### 弹框(BaseDialogModel)也要双变体,最容易漏
|
||||
|
||||
页面布局适配完,长按/批量操作弹出的 DialogModel 仍会以 Pad 居中样式出现在手机上
|
||||
(出港运抵的「状态重置」就漏过)。做法与页面完全同构:
|
||||
|
||||
1. dialog 布局 `git mv` 到 `layout-sw600dp/`,`layout/` 下写同名手机版(设计稿通常是底部弹层:
|
||||
`bg_phone_panel_top` + 居中标题 + `ic_phone_close` + 全宽 `bg_phone_btn_primary` 确定键)
|
||||
2. 弹出位置在构造参数按形态切换(XPopup 位置与布局是两回事,都要改):
|
||||
|
||||
```kotlin
|
||||
class XxxDialogModel(...) : BaseDialogModel<DialogXxxBinding>(
|
||||
if (DeviceUtil.isPhone()) DIALOG_TYPE_BOTTOM else DIALOG_TYPE_CENTER
|
||||
)
|
||||
```
|
||||
|
||||
3. 两变体 `<variable>` 一致(imports 不要求一致)
|
||||
|
||||
参考:`IntExpArriveResetDialogModel` + `dialog_int_exp_arrive_reset.xml` 双变体。
|
||||
**适配收尾时逐个 grep 该页 ViewModel 里 `DialogModel(` 的调用点**,每个弹框都过一遍。
|
||||
|
||||
## 业务层零破坏的三个模式
|
||||
|
||||
### 新增查询维度:默认空值 + 仅非空进请求
|
||||
|
||||
平板布局没有对应控件 → 字段恒为空 → 不进请求体 → 平板查询与改造前逐字节一致。
|
||||
|
||||
```kotlin
|
||||
val handoverState = MutableLiveData("") // 手机 Tab 绑这个,平板无控件
|
||||
val hoId = MutableLiveData("")
|
||||
|
||||
val filterParams = mapOf(
|
||||
"fdate" to flightDate.value?.ifEmpty { null },
|
||||
/* 原有字段… */
|
||||
"hoState" to handoverState.value?.ifEmpty { null }, // 空 → null → 不影响平板
|
||||
"hoId" to hoId.value?.ifEmpty { null },
|
||||
)
|
||||
```
|
||||
|
||||
### 手机专属的额外请求:ViewModel 内部开关
|
||||
|
||||
Tab 角标计数、新增下拉字典这类请求,平板不需要,多发就是改变了平板行为(请求次数)。
|
||||
用一个内部布尔控制,由 Activity 在手机形态下开启:
|
||||
|
||||
```kotlin
|
||||
// ViewModel
|
||||
private var phoneExtrasEnabled = false
|
||||
|
||||
fun initPhoneExtras() {
|
||||
phoneExtrasEnabled = true
|
||||
handoverState.value = "0" // 手机默认停在第一个 Tab
|
||||
DictUtils.getWHSUserList(addAll = false) { hoUserList.postValue(it) }
|
||||
}
|
||||
|
||||
override fun getData() {
|
||||
/* 原有列表 + 统计请求… */
|
||||
if (phoneExtrasEnabled) loadTabCounts(filterParams) // 平板不触发
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// Activity
|
||||
if (DeviceUtil.isPhone()) viewModel.initPhoneExtras()
|
||||
```
|
||||
|
||||
`DeviceUtil.isPhone()` 只用来决定**数据默认值和额外请求**,**永远不用来选布局**。
|
||||
|
||||
### 纯 UI 状态放 Activity
|
||||
|
||||
弹层显隐、Tab 高亮这类与业务无关的状态,用 `ObservableBoolean` 放在 Activity 上暴露给 XML,
|
||||
不要污染 ViewModel:
|
||||
|
||||
```kotlin
|
||||
val filterPanelVisible = ObservableBoolean(false)
|
||||
fun toggleFilterPanel() = filterPanelVisible.set(!filterPanelVisible.get())
|
||||
```
|
||||
|
||||
### 同名 id 在两个变体里必须是同一种控件
|
||||
|
||||
平板变体的 `btnMove` 是 `TextView`,手机版底部换成了 `PhoneBottomBar`——**不要为了复用
|
||||
Activity 里的 `binding.btnMove` 而把这个 id 安到 PhoneBottomBar 上**。同名不同类型会让
|
||||
DataBinding 把字段类型退化成公共父类,编译期未必报错,运行时行为难料。
|
||||
|
||||
正确做法:手机端的按钮点击走组件自身的 `setOnActionClick`,`btnMove` 就只留在平板变体里,
|
||||
Activity 侧判空调用:
|
||||
|
||||
```kotlin
|
||||
binding.btnMove?.setOnClickListener { showMoveConfirmDialog() }
|
||||
```
|
||||
|
||||
被 XML 调用的 Activity 方法记得改成 public。
|
||||
|
||||
### 调字典要对齐既有页面的参数口径
|
||||
|
||||
`DictUtils` 的方法参数常有"看起来合理但后端不认"的取值。出港移库的特码下拉一开始传
|
||||
`ieFlag="E"`(出港),接口返回空数组;而同模块既有页面(`GjcWeighingStartViewModel` 等)
|
||||
一律传 `ieFlag=""`,能正常返回。
|
||||
|
||||
所以调字典前先 grep 一下同模块其他页面怎么调的,照抄它们的参数;改完必须在实机上
|
||||
**把下拉点开确认真的有数据**,空列表在 UI 上看不出报错。
|
||||
|
||||
### 共享方法别为手机改语义
|
||||
|
||||
`checkAllClick()` 这类平板也在用的方法,不要为了手机端的语义去改它。
|
||||
例:手机「待交接」Tab 下理论上只有未交接记录(服务端按状态过滤),所以全选照原样全量勾选就是对的;
|
||||
若为此加上「跳过已交接」的过滤,反而改变了平板端(平板列表混排两种状态)的行为。
|
||||
|
||||
## 屏幕方向:Manifest 一律写 `unspecified`
|
||||
|
||||
新增 Activity 注册时:
|
||||
|
||||
```xml
|
||||
<activity android:name="…"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="false"
|
||||
android:screenOrientation="unspecified" />
|
||||
```
|
||||
|
||||
**两种错误方案都实测踩过,别再试:**
|
||||
- 写死 `userLandscape` → 手机「先横屏、约 1 秒后转竖屏」(方向在建窗口时就定了,
|
||||
`BaseActivity` 在 `super.onCreate` 之前纠正也来不及)
|
||||
- 用 `@integer/screen_orientation` 资源引用(曾经以为是优雅方案)→ **更糟**:系统解析
|
||||
Manifest 属性不套用设备限定符,所有 Activity 实际都拿到 `values/` 默认值(竖屏),
|
||||
平板因此先竖后横,且 AutoSize 在竖屏配置下选中平板竖屏 720dp 基准
|
||||
(正常应为 1152×720 横屏基准),交互几次后整个 UI 放大约 1.6 倍
|
||||
|
||||
`unspecified` 让系统按设备当前物理方向建窗口(平板横屏摆放→横屏起,手机→竖屏起,
|
||||
两端起始方向本就正确),`BaseActivity.applyDeviceOrientation()` 再按形态锁定,
|
||||
不产生二次纠正。全项目 192 处已统一为 `unspecified`。
|
||||
|
||||
## 改 module_base 公共代码时
|
||||
|
||||
`module_base` 被所有业务模块依赖,改动会影响全部页面。原则:
|
||||
|
||||
- **只做增量**:加新组件、新颜色、新 drawable,别改既有组件的行为
|
||||
- 确实要改公共基类(`BaseActivity` / `BaseBindingActivity` / Pad 系组件)时,
|
||||
**必须在平板上回归**至少 2~3 个不同类型的既有页面
|
||||
- Bean 上加只读计算属性是安全的(Gson 只序列化字段,不动 getter);
|
||||
加**字段**要谨慎,会进请求体
|
||||
|
||||
## RecyclerView 里不要用 item 级 visibility 做 Tab/分类过滤
|
||||
|
||||
普通 ViewGroup(LinearLayout 等)里 `View.GONE` 的子 View 会自动塌陷为 0 高度、不占布局空间——
|
||||
但 **RecyclerView + LinearLayoutManager 不是这样**:它按子项的测量高度参与滚动区域计算,
|
||||
不会因为子项是 GONE 就跳过。给 item 根节点加 `visibility="@{tab条件 ? VISIBLE : GONE}"`
|
||||
切 Tab,会在列表里留下一段空白(隐藏项的高度仍被保留),角标数字和实际卡片数量对不上。
|
||||
|
||||
出港运抵的「待运抵/已运抵」Tab 就踩了这个坑(实机截图验证:切到「已运抵」后顶部有一大块
|
||||
空白,隐藏的待运抵卡片高度并未塌陷)。正确做法是在数据层过滤,而不是视图层:
|
||||
|
||||
```kotlin
|
||||
// ViewModel:自己维护完整列表,不依赖 adapter.items 的历史状态
|
||||
// (见下一节:adapter.items 在 loadMore 场景可能已经是上次过滤后的子集,不能当作真源)
|
||||
private var allLoadedList: List<GjcMaWb> = emptyList()
|
||||
|
||||
override fun getData() {
|
||||
launchLoadingCollect({ ... }) {
|
||||
onSuccess = { result ->
|
||||
val newPageItems = result.list ?: emptyList()
|
||||
allLoadedList = if (pageModel.page <= 1) newPageItems else allLoadedList + newPageItems
|
||||
|
||||
pageModel.handleListBean(result) {
|
||||
// 无论 handleListBean 内部把 adapter.items 搞成什么样,这里用 allLoadedList
|
||||
// 强制覆盖为「当前 Tab 的正确子集」,兜底纠正
|
||||
val filtered = allLoadedList.filter { it.isPending == (currentTab.value == "pending") }
|
||||
pageModel.rv?.commonAdapter()?.refresh(filtered)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onTabChangedPhone() {
|
||||
val filtered = allLoadedList.filter { it.isPending == (currentTab.value == "pending") }
|
||||
pageModel.rv?.commonAdapter()?.refresh(filtered) // 只是 items.clear()+addAll()+notifyDataSetChanged,无副作用
|
||||
}
|
||||
```
|
||||
|
||||
`CommonAdapter.refresh()` 只是替换内部列表并 `notifyDataSetChanged()`,不影响
|
||||
SmartRefreshLayout 的分页/完成状态,可以放心在 `handleListBean` 回调里再调用一次。
|
||||
|
||||
## Intent 传递 Serializable Bean:`@Transient` 字段反序列化后是 null
|
||||
|
||||
`GjcMaWb`/`GjcHaWb` 的 `checked`/`showMore`/`actionMenuVisible` 三个 UI 状态字段都标了
|
||||
`@Transient`(本意是避免 Gson 把 `ObservableBoolean` 的内部结构序列化进网络请求体)。
|
||||
但 Kotlin 的 `@Transient` 注解生成的就是 JVM 的 `transient` 修饰符,这个修饰符对 **Java
|
||||
对象序列化**(`Intent.putExtra(String, Serializable)` 走的正是这条路径)有实际含义:
|
||||
反序列化时会跳过 `transient` 字段的恢复,对象引用类型的字段结果就是 **null**。
|
||||
|
||||
新增手机专属页面时,如果要把 `List<GjcHaWb>` 通过 `ArrayList<GjcHaWb>` 塞进 `Intent`
|
||||
传给下一个页面,接收方拿到的对象「看起来」字段都对(wbNo、pc、weight 这些正常字段没问题,
|
||||
因为它们不是 `@Transient`),但只要调用 `bean.checked.set(...)` 就会立刻崩溃:
|
||||
|
||||
```
|
||||
NullPointerException: Attempt to invoke virtual method
|
||||
'void androidx.databinding.ObservableBoolean.set(boolean)' on a null object reference
|
||||
```
|
||||
|
||||
出港运抵的「分单明细」页第一版全选按钮就是这样崩的。**修复只需一行**,在接收方
|
||||
`initOnCreated` 里对拿到的列表整体 `.copy()` 一遍——`.copy()` 会重新触发一次真正的对象构造,
|
||||
类体里 `val checked = ObservableBoolean(false)` 这类初始化逻辑跟着重新跑一遍,
|
||||
把 null 补成全新实例:
|
||||
|
||||
```kotlin
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun initOnCreated(intent: Intent) {
|
||||
val list = intent.getSerializableExtra(Constant.Key.DATA) as? ArrayList<GjcHaWb>
|
||||
// .copy() 补上被 Java 序列化跳过的 @Transient 字段
|
||||
fullList = (list ?: emptyList()).map { it.copy() }
|
||||
}
|
||||
```
|
||||
|
||||
这个坑只在「Intent 传递 Serializable Bean + 接收方要切换 checked/showMore 等状态」时才会
|
||||
触发;纯只读展示(如回执页)不调用这些字段的 setter,不会崩,但补一道 `.copy()` 无害,
|
||||
建议只要通过 Intent 传了 haWbList/maWbList 就统一加上,别等下次踩到才想起来。
|
||||
|
||||
## 已知的既有问题(不是你改坏的)
|
||||
|
||||
用 `am start` 直启页面时,若**先经过首页**再启目标页,会崩在
|
||||
`LoadingModel.showLoading` → XPopup `popupInfo is null`。在**未改动**的页面上同样复现,
|
||||
属于既有缺陷。**冷启动直启不受影响**(`force-stop` 后直接 `am start`)。
|
||||
|
||||
遇到崩溃先做这个对照实验再下结论:拿一个你没碰过的页面用同样方式打开,看是否同样崩。
|
||||
80
.claude/skills/phone-adapt/references/proxyman.md
Normal file
80
.claude/skills/phone-adapt/references/proxyman.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Proxyman MCP:抓包 / 直调 / Mock(阶段 2、5、6 的调试利器)
|
||||
|
||||
Proxyman 桌面端 + `mcp__proxyman__*` 工具(会话内直接可用,缺 schema 时用 ToolSearch
|
||||
`select:mcp__proxyman__get_flows,...` 加载)。**2026-07-31 已在出港查询页全链路实测**:
|
||||
抓包核对请求体、Map Local mock 三种卡片形态(含真实数据不存在的「未入库」兜底态)一次跑通。
|
||||
|
||||
## 与 api-doc 的分工(先契约后事实)
|
||||
|
||||
| | api-doc MCP | Proxyman MCP |
|
||||
|---|---|---|
|
||||
| 回答什么 | **契约**:接口该有什么入参/出参、参数名叫什么 | **事实**:App 实际发了什么、后端实际回了什么 |
|
||||
| 用在何时 | 阶段 2 开工前逐接口核对 | 阶段 5/6 实机验证与 corner case 补测 |
|
||||
|
||||
标准配合流程:
|
||||
api-doc 拿权威参数名 → 前端实现 → Proxyman flow 核对请求体确实带上了 →
|
||||
compose 改参 A/B 验证后端认不认 → 不认 → 记问题清单(附 `export_flow_curl` 证据)→
|
||||
map local 造 mock 补 UI corner case 验证。
|
||||
|
||||
## 接入(一次性,实测步骤)
|
||||
|
||||
```bash
|
||||
# 1) 确认 Proxyman 在跑(返回 Recording: Active + 端口,默认 9090)
|
||||
# mcp__proxyman__get_proxy_status
|
||||
# 2) 模拟器全局代理指向宿主机(模拟器视角宿主机 = 10.0.2.2)
|
||||
adb -s <serial> shell settings put global http_proxy 10.0.2.2:9090
|
||||
# 3) 验证完【必须】清掉,否则 Proxyman 一关模拟器就断网,下次验证莫名全挂
|
||||
adb -s <serial> shell settings put global http_proxy :0
|
||||
```
|
||||
|
||||
本项目内网服务是**纯 HTTP**(192.168.1.250:8093)→ 不需要装证书,明文直接可见。
|
||||
(若未来切 HTTPS,才需要 `install_certificate` + `enable_ssl_proxying`。)
|
||||
|
||||
## 场景 1:核对请求/响应(替代 `ui_probe req`)
|
||||
|
||||
logcat 抓 OkHttp 多行 pretty JSON 脆弱且要人肉拼接;Proxyman 一步到位:
|
||||
|
||||
- `filter_flows`(key=url, value="IntExpSearch")→ 拿 flow ID 列表
|
||||
- `get_flow_detail`(flow_id)→ 完整请求体 + 响应体 + headers(Authorization 自动脱敏)
|
||||
|
||||
**出参无文档定义时(如 `IntExpSearch/detail` 返回裸 Map),用真实 flow 的 responseBody
|
||||
直接盘点字段清单**——判定「锁定状态/计费重量是否存在」这类问题的最快路径。
|
||||
|
||||
## 场景 2:直调后端 A/B 验证(替代 run-as 提 token + 手写 curl)
|
||||
|
||||
`create_compose_http_from_flow`(从真实 flow 复制请求,**token 自动带上,不用碰
|
||||
shared_prefs**)→ `update_compose_http` 改一个参数 → `send_compose_http` 看结果对比。
|
||||
isFclose 这类「参数是否生效」的 A/B 实验用这条链。给后端提缺口时用
|
||||
`export_flow_curl` 导出可复现的 curl 附进问题清单。
|
||||
|
||||
## 场景 3:Map Local 造 mock 验证 corner case(替代代码注入样例 Bean)
|
||||
|
||||
老流程「Activity 临时注入 Bean → 截图 → 删码 → 重构建」彻底淘汰:**零代码改动、零重构建**。
|
||||
|
||||
- `create_map_local`:url 精确到接口路径,method=POST,response_body 直接写目标 JSON
|
||||
(构造空字段/超长文本/罕见状态,比如出港查询的 fclose+opDate 双空「未入库」兜底态)
|
||||
- 改真实响应个别字段更省事时用 `create_map_local_from_flow`
|
||||
- 验证完 `delete_rule`(rule_type=maplocal)+ 清代理,**规则残留会污染后续真实数据验证**
|
||||
|
||||
响应结构照抄真实 flow 的顶层结构(如 pageQuery 是裸 PageInfo:`{total, list, pages, ...}`,
|
||||
不要包 `{status, data}`)。
|
||||
|
||||
⚠️ 实测坑(2026-07-31 出港仓库):mock `BaseResultBean` 包装的接口(pageQueryTotal/detail 等)时,
|
||||
**`status` 必须写 `"1"`**——`BaseResultBean.verifySuccess()` 判 `status == "1"`,写 "200" 会被当失败
|
||||
静默丢弃(UI 保持默认值,无任何报错,排查靠 flow 已命中但 UI 不变这一矛盾现象)。
|
||||
|
||||
⚠️ 实测坑:mock 响应到达后 UI 渲染可能滞后(冷启动首帧 Davey 卡顿 2s+),
|
||||
**截图断言前先 `ui_probe texts` 确认数据已上屏**——这次就因截图太早误判过一轮「mock 没生效」,
|
||||
其实 `get_flow_detail` 里 `matchedTools: ["Map Local"]` 早已证明规则命中,UI 只是还没画完。
|
||||
|
||||
## 场景 4:环境模拟(按需)
|
||||
|
||||
- `create_network_condition`:弱网/高延迟,验证 loading 弹窗与超时提示
|
||||
- `create_breakpoint`:单次拦截改包(比 map local 更临时的一次性实验)
|
||||
- `create_map_remote`:把请求转发到另一环境(如后端修复验证用的测试实例)
|
||||
|
||||
## 验证收尾清单
|
||||
|
||||
- [ ] `list_rules` 确认无本次遗留规则(maplocal/breakpoint/network_condition)
|
||||
- [ ] 模拟器代理已清(`settings get global http_proxy` 应为 `:0` 或空)
|
||||
- [ ] 用真实数据再过一遍被 mock 过的页面,确认无假数据残影
|
||||
181
.claude/skills/phone-adapt/references/verification.md
Normal file
181
.claude/skills/phone-adapt/references/verification.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# 端到端验证手册
|
||||
|
||||
目标:在**真实接口**下把手机端走通,并确认平板端没退化。只看编译通过是不够的——
|
||||
本次适配里"筛选条件被静默清空"和"后端不认新参数"两个问题,都只有跑真机才暴露。
|
||||
|
||||
## 环境
|
||||
|
||||
```bash
|
||||
~/Library/Android/sdk/emulator/emulator -list-avds # Medium_Phone_API_36.1(手机) / Aerologic_Tablet(平板)
|
||||
adb devices -l
|
||||
adb -s <serial> shell wm size && adb -s <serial> shell wm density
|
||||
```
|
||||
|
||||
判定形态:手机 1080×2400@420dpi → `PHONE(sw=411dp)`;平板 1280×800@160dpi → `TABLET(sw=800dp)`。
|
||||
|
||||
`ui_probe.sh` 封装了常用操作,比手写 adb 省事且更可靠:
|
||||
|
||||
```bash
|
||||
P=.claude/skills/phone-adapt/scripts/ui_probe.sh
|
||||
$P <serial> form # 打印形态识别结果
|
||||
$P <serial> texts # 当前界面所有可见文字(做断言用)
|
||||
$P <serial> bounds "已交接" # 元素 bounds + 中心点
|
||||
$P <serial> tap-text "确认" # 按文字点击(自动取中心点)
|
||||
$P <serial> wait-text "待交接" # 等页面就绪
|
||||
$P <serial> wait-gone "请稍候……" # 等加载弹窗消失
|
||||
$P <serial> shot /tmp/a.png # 截图
|
||||
$P <serial> req pageQuery # 抓请求/响应体
|
||||
$P <serial> crash # 崩溃次数 + 栈
|
||||
```
|
||||
|
||||
## 构建安装
|
||||
|
||||
```bash
|
||||
./gradlew assembleDebug 2>&1 | grep -E "^(BUILD|FAILURE)|error:"
|
||||
adb -s <serial> install -r -d app/build/outputs/apk/debug/app-debug.apk
|
||||
```
|
||||
|
||||
注意 gradle 输出里大量 `e: 注: ARouter::Compiler …` 是注解处理器的**提示**被误标成 `e:`,
|
||||
不是错误;只看 `BUILD SUCCESSFUL` / `error:` 就行。
|
||||
|
||||
## 进入目标页面的两条路
|
||||
|
||||
### 正规路径(优先):登录 → 首页菜单
|
||||
|
||||
```bash
|
||||
adb -s <serial> shell monkey -p com.lukouguoji.aerologic -c android.intent.category.LAUNCHER 1
|
||||
```
|
||||
|
||||
测试环境账号在记忆文件 `test-env-login.md`(ADMIN / 用户已授权保存),可自主登录。
|
||||
填表用 `input text`,**`#` 不需要转义**——新版 adb 的 input text 参数不经远端 shell 解析,
|
||||
写 `\#` 反而会把 `\` 也输进去(实测多出一位)。
|
||||
|
||||
⚠️ **「记住密码」回填哈希坑(实测踩过)**:输入用户名后 App 会调 `queryUserByName`
|
||||
把本地存储的 **bcrypt 哈希**回填进密码框,盖掉/混入你输入的明文 → 登录 200 但校验失败、
|
||||
静默停在登录页(密码框点数对不上是判断信号:明文 9 位 vs 回填后 10 位显示)。
|
||||
正确顺序:点密码框 → 等回填(sleep 1)→ `input keycombination 113 29`(CTRL+A)+
|
||||
`KEYCODE_DEL` 清空 → 再输明文 → `KEYCODE_BACK` 收键盘 → 点登录。
|
||||
键盘不收起时「登录」按钮可能点不到(tap 被吞),收起后再点。
|
||||
|
||||
底部有「首页 / 国内 / 国际 / 我的」Tab,国际业务页找对应入口
|
||||
(底部 Tab 的「国际」与顶部筛选的「国际」同名,`tap-text "国际" 2` 指定第 2 处)。
|
||||
|
||||
**首页菜单是按权限过滤的**:当前账号角色没有该权限时,入口根本不显示(本次 ADMIN 登录后
|
||||
国际业务只有「板箱过磅」「进港理货」,没有「出库交接」)。这时走下面的直启。
|
||||
|
||||
### 直启(权限缺失时的替代):冷启动 + am start
|
||||
|
||||
`app/src/debug/AndroidManifest.xml` 里把调试页临时 `exported="true"`,就能直接拉起。
|
||||
**必须冷启动**(先 force-stop),否则会撞上既有的 LoadingModel 崩溃:
|
||||
|
||||
```bash
|
||||
adb -s <serial> shell am force-stop com.lukouguoji.aerologic
|
||||
sleep 2
|
||||
adb -s <serial> shell am start -n com.lukouguoji.aerologic/<完整类名>
|
||||
```
|
||||
|
||||
登录态存在 SharedPreference 里,冷启动直启同样带 token,接口能正常返回真实数据。
|
||||
|
||||
> 先经首页再 `am start` 会崩在 `LoadingModel.showLoading` → XPopup `popupInfo is null`,
|
||||
> 这是既有缺陷(未改动的页面也复现),不是本次改动引入的。
|
||||
|
||||
## 点击前先等就绪
|
||||
|
||||
加载弹窗("请稍候……")会盖住整屏并**吞掉 `input tap`**,表现为"点了没反应"。
|
||||
本次验证就因此白跑了几轮。所以:
|
||||
|
||||
```bash
|
||||
$P <serial> wait-text "待交接" # 等目标元素出现
|
||||
$P <serial> tap-text "已交接"
|
||||
$P <serial> texts | grep -c "全选" # 用文字变化确认点击确实生效
|
||||
```
|
||||
|
||||
点完**一定要断言效果**(用 `texts` 或截图),别假设点中了。
|
||||
|
||||
## 逐项走查清单
|
||||
|
||||
每一步做完都跑一次 `crash`(必须为 0):
|
||||
|
||||
- [ ] 列表加载:卡片字段、各种状态形态(如待处理 / 已完成两种卡片)
|
||||
- [ ] Tab 切换:指示条位置、角标数字、底部条按设计显隐
|
||||
- [ ] 筛选弹层:打开、各控件取值、重置、确认后重新查询
|
||||
- [ ] 下拉字典:真实取到数据(不是空列表)
|
||||
- [ ] 多选:单选、全选联动、"已选 N 项"计数
|
||||
- [ ] 详情页/二级页:字段绑定、条件显示的区块
|
||||
- [ ] 扫码入口能拉起
|
||||
|
||||
## 核对请求参数是否真的生效
|
||||
|
||||
新增的查询字段要确认两件事:客户端**发出去了**,服务端**认**。
|
||||
|
||||
```bash
|
||||
$P <serial> req pageQuery
|
||||
```
|
||||
|
||||
OkHttp 日志是多行 pretty JSON。看到请求体里有你的字段(如 `"handoverState": "0"`)只说明客户端对了。
|
||||
**服务端是否生效要用行为验证**:切到另一个 Tab,看返回数据是否真的变了。
|
||||
|
||||
⚠️ 行为验证失败时**先怀疑参数名,再怀疑后端**:出库交接第一版传 `hoState`,两个 Tab 返回同一批数据,
|
||||
一度误判为「后端没做过滤」;实际是参数名错了(api-doc 里是 `handoverState`),后端对不认识的参数
|
||||
**静默忽略**。正确顺序:查 `references/api-doc.md` 核对参数名 → 用 token 直调后端做 A/B 对比
|
||||
(同条件只改一个参数看 total 变化)→ 仍不生效才定性为后端缺陷并记入
|
||||
`documents/后端接口对接问题清单.xlsx`。
|
||||
|
||||
## 无数据时怎么核对 UI
|
||||
|
||||
服务端当天可能没数据(列表空)。放宽条件(清空日期)再查;仍为空时,临时注入样例 Bean:
|
||||
|
||||
```kotlin
|
||||
// 临时:覆盖各种形态各一条,验证后删除
|
||||
binding.rv.postDelayed({
|
||||
binding.rv.commonAdapter()?.refresh(listOf(/* 待处理 / 危险品 / 已完成 各一条 */))
|
||||
}, 2500)
|
||||
```
|
||||
|
||||
截图核对完**必须删除**,然后重新构建 + grep 确认无残留:
|
||||
|
||||
```bash
|
||||
grep -rn "injectMockForVisualCheck\|TODO 临时" module_base/src module_gjc/src | wc -l # 应为 0
|
||||
```
|
||||
|
||||
## 屏幕方向验证
|
||||
|
||||
新增/修改 Activity 后,确认手机端不会「先横屏再转竖屏」:
|
||||
|
||||
```bash
|
||||
# 冷启动时连续采样:应全程 mRotation=0
|
||||
adb -s <phone> shell am force-stop com.lukouguoji.aerologic; sleep 2
|
||||
adb -s <phone> shell monkey -p com.lukouguoji.aerologic -c android.intent.category.LAUNCHER 1
|
||||
for i in $(seq 1 10); do adb -s <phone> shell dumpsys window | grep -m1 -oE "mRotation=[0-9]+"; sleep 0.4; done
|
||||
```
|
||||
|
||||
平板则应首屏就是横屏(截图宽 > 高)。若手机出现横屏帧,检查该 Activity 的 Manifest
|
||||
是否写成了别的值——**必须是 `android:screenOrientation="unspecified"`**(写死 `userLandscape`
|
||||
或用 `@integer/...` 资源引用都会出问题,详见 SKILL.md 阶段 4)。
|
||||
|
||||
## Pad 端回归(必做)
|
||||
|
||||
```bash
|
||||
adb -s <tablet> install -r -d app/build/outputs/apk/debug/app-debug.apk
|
||||
adb -s <tablet> shell am start -n com.lukouguoji.aerologic/<同一个类名>
|
||||
$P <tablet> shot /tmp/tablet.png
|
||||
$P <tablet> form # 应为 TABLET(sw=800dp…)
|
||||
```
|
||||
|
||||
对比改造前后:搜索条数量与顺序、日期默认值、按钮(平板可能比手机多按钮)、底部统计文案。
|
||||
改过 `module_base` 里的公共基类或 Pad 系组件时,这一步不能省,且要多看几个页面。
|
||||
|
||||
## 定位"值被神秘修改"类问题
|
||||
|
||||
在 setter 里打带堆栈的日志,一次就能看到调用来源:
|
||||
|
||||
```kotlin
|
||||
android.util.Log.d("DBG", "value '$field' -> '$v'", Throwable("trace"))
|
||||
```
|
||||
|
||||
```bash
|
||||
adb -s <serial> logcat -d -s DBG | grep -A14 "'旧值' -> ''" | grep "at com.lukouguoji\|at android"
|
||||
```
|
||||
|
||||
本次靠这招定位到 `TextView.onRestoreInstanceState → EditText.setText → doOnTextChanged`,
|
||||
即复合自定义 View 的 id 串档(见 `components.md`)。**排完删日志。**
|
||||
137
.claude/skills/phone-adapt/scripts/design_shots.py
Executable file
137
.claude/skills/phone-adapt/scripts/design_shots.py
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
把 5.5 寸手持端设计稿(HTML 原型)渲染成截图,供逐像素比对。
|
||||
|
||||
设计稿是带 Tailwind + JS 的交互原型:列表页、Tab、详情页、筛选弹层往往挤在同一个
|
||||
HTML 里,靠 JS 函数切换。只读源码容易漏掉视觉细节(间距、圆角、真实配色、
|
||||
以及 JS 里 id 映射写错导致的字段错位),所以务必渲染成图来看。
|
||||
|
||||
用法:
|
||||
# 1) 先列出 HTML 里可用的状态切换函数与元素 id,据此决定要截哪些状态
|
||||
python3 design_shots.py --html 出库交接.html --list
|
||||
|
||||
# 2) 渲染:默认状态 + 注入 JS 触发的各个状态
|
||||
python3 design_shots.py --html 出库交接.html --out /tmp/shots \
|
||||
--state "01_列表:" \
|
||||
--state "02_已交接Tab:switchTab('shipped');" \
|
||||
--state "03_详情:showDetail({id:'PMC1',status:'已交接'});" \
|
||||
--state "04_筛选弹层:toggleFilterDrawer(true);"
|
||||
|
||||
每个 --state 形如 "名称:JS",JS 可为空表示页面初始态。
|
||||
输出 <out>/<名称>.png,同时保留注入后的 <名称>.html 便于复查。
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.parse
|
||||
|
||||
CHROME_CANDIDATES = [
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
||||
shutil.which("google-chrome") or "",
|
||||
shutil.which("chromium") or "",
|
||||
shutil.which("chromium-browser") or "",
|
||||
]
|
||||
|
||||
|
||||
def find_chrome() -> str:
|
||||
for path in CHROME_CANDIDATES:
|
||||
if path and os.path.exists(path):
|
||||
return path
|
||||
sys.exit(
|
||||
"找不到 Chrome/Chromium。请安装,或用 --chrome 指定可执行文件路径。"
|
||||
)
|
||||
|
||||
|
||||
def list_hooks(html: str) -> None:
|
||||
"""列出可用于构造状态的 JS 函数和元素 id。"""
|
||||
funcs = sorted(set(re.findall(r"function\s+([A-Za-z_$][\w$]*)\s*\(", html)))
|
||||
onclicks = sorted(set(re.findall(r'onclick="([^"]+)"', html)))
|
||||
ids = sorted(set(re.findall(r'id="([^"]+)"', html)))
|
||||
|
||||
print("== JS 函数(可直接注入调用)==")
|
||||
for f in funcs:
|
||||
print(f" {f}()")
|
||||
print("\n== onclick 表达式(含真实参数,照抄最省事)==")
|
||||
for o in onclicks[:40]:
|
||||
print(f" {o}")
|
||||
if len(onclicks) > 40:
|
||||
print(f" ... 其余 {len(onclicks) - 40} 条略")
|
||||
print("\n== 元素 id(判断有哪些页面/弹层)==")
|
||||
print(" " + ", ".join(ids))
|
||||
|
||||
|
||||
def render(chrome: str, html: str, name: str, js: str, out_dir: str,
|
||||
width: int, height: int, scale: float, budget: int) -> str:
|
||||
# 等 load 之后再触发,确保 CDN 的 Tailwind/Iconify 已生效
|
||||
inject = (
|
||||
"<script>window.addEventListener('load',function(){"
|
||||
f"setTimeout(function(){{{js}}},300);}});</script>"
|
||||
)
|
||||
page_path = os.path.join(out_dir, f"{name}.html")
|
||||
with open(page_path, "w", encoding="utf-8") as fp:
|
||||
fp.write(html.replace("</body>", inject + "</body>"))
|
||||
|
||||
png_path = os.path.join(out_dir, f"{name}.png")
|
||||
subprocess.run(
|
||||
[
|
||||
chrome,
|
||||
"--headless=new",
|
||||
"--disable-gpu",
|
||||
"--hide-scrollbars",
|
||||
f"--force-device-scale-factor={scale}",
|
||||
f"--window-size={width},{height}",
|
||||
f"--virtual-time-budget={budget}",
|
||||
f"--screenshot={png_path}",
|
||||
"file://" + urllib.parse.quote(os.path.abspath(page_path)),
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
return png_path
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--html", required=True, help="设计稿 HTML 路径")
|
||||
ap.add_argument("--out", default="/tmp/design_shots", help="输出目录")
|
||||
ap.add_argument("--state", action="append", default=[],
|
||||
help='形如 "名称:JS",可重复;JS 为空表示初始态')
|
||||
ap.add_argument("--list", action="store_true",
|
||||
help="只列出可用的 JS 函数 / onclick / id,不截图")
|
||||
ap.add_argument("--chrome", default="", help="Chrome 可执行文件路径")
|
||||
# 原型容器一般是 375×812 的手机壳,留出边距后 460×900 刚好完整收进去
|
||||
ap.add_argument("--width", type=int, default=460)
|
||||
ap.add_argument("--height", type=int, default=900)
|
||||
ap.add_argument("--scale", type=float, default=2.0, help="缩放倍数,2 便于看清细节")
|
||||
ap.add_argument("--budget", type=int, default=9000,
|
||||
help="虚拟时间预算(ms),需覆盖 CDN 脚本加载")
|
||||
args = ap.parse_args()
|
||||
|
||||
with open(args.html, encoding="utf-8") as fp:
|
||||
html = fp.read()
|
||||
|
||||
if args.list:
|
||||
list_hooks(html)
|
||||
return
|
||||
|
||||
chrome = args.chrome or find_chrome()
|
||||
os.makedirs(args.out, exist_ok=True)
|
||||
|
||||
states = args.state or ["01_default:"]
|
||||
for spec in states:
|
||||
name, _, js = spec.partition(":")
|
||||
png = render(chrome, html, name.strip(), js, args.out,
|
||||
args.width, args.height, args.scale, args.budget)
|
||||
ok = os.path.exists(png) and os.path.getsize(png) > 0
|
||||
size = os.path.getsize(png) if ok else 0
|
||||
print(f"{'✓' if ok else '✗'} {name.strip():<24} {png} ({size} bytes)")
|
||||
|
||||
print(f"\n用 Read 工具逐张查看 {args.out}/*.png,再开始写布局。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
119
.claude/skills/phone-adapt/scripts/ui_probe.sh
Executable file
119
.claude/skills/phone-adapt/scripts/ui_probe.sh
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
# 实机验证辅助:读取界面文字 / 按文字点击 / 截图 / 抓请求体。
|
||||
#
|
||||
# 为什么需要它:手机端验证要反复点 Tab、弹层按钮、卡片链接。靠肉眼估坐标点击
|
||||
# 极易点空(本次适配就因此白跑了几轮),而 uiautomator 能拿到元素真实 bounds。
|
||||
# tap-text 直接按文字定位并点中心,比手算坐标可靠得多。
|
||||
#
|
||||
# 用法:
|
||||
# ui_probe.sh <serial> texts 列出当前界面所有可见文字
|
||||
# ui_probe.sh <serial> bounds "已交接" 打印该文字元素的 bounds 与中心点
|
||||
# ui_probe.sh <serial> tap-text "确认" [序号] 按文字点击(取中心点);同名多处时默认第 1 处
|
||||
# ui_probe.sh <serial> wait-text "待交接" [秒] 等文字出现(默认 20 秒),点击前先等页面就绪
|
||||
# ui_probe.sh <serial> wait-gone "请稍候……" [秒] 等文字消失(如等加载弹窗关闭)
|
||||
# ui_probe.sh <serial> shot /tmp/a.png 截图到本地
|
||||
# ui_probe.sh <serial> req <关键字> 从 logcat 抓含关键字的请求/响应体
|
||||
# ui_probe.sh <serial> crash 统计并打印最近一次崩溃栈
|
||||
# ui_probe.sh <serial> form 打印形态识别结果 PHONE/TABLET
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
SERIAL="${1:?用法: ui_probe.sh <serial> <命令> [参数]}"
|
||||
CMD="${2:?缺少命令}"
|
||||
ARG="${3:-}"
|
||||
PKG="com.lukouguoji.aerologic"
|
||||
A="adb -s $SERIAL"
|
||||
|
||||
dump() {
|
||||
$A shell uiautomator dump /sdcard/_probe.xml >/dev/null 2>&1
|
||||
# 注意:dump 出来的 XML 是一整行,必须按 '<' 拆成一节点一行才能按节点过滤。
|
||||
# (`tr '>' '>\n'` 是字符映射不是字符串替换,等于空操作,别踩这个坑。)
|
||||
$A shell cat /sdcard/_probe.xml | tr '<' '\n'
|
||||
}
|
||||
|
||||
# 打印所有 text 完全等于 $1 的节点的 bounds 与中心点
|
||||
centers_of() {
|
||||
dump | grep -F "text=\"$1\"" \
|
||||
| grep -oE 'bounds="\[[0-9]+,[0-9]+\]\[[0-9]+,[0-9]+\]"' \
|
||||
| sed -E 's/bounds="\[([0-9]+),([0-9]+)\]\[([0-9]+),([0-9]+)\]"/\1 \2 \3 \4/' \
|
||||
| awk '{printf "bounds=[%d,%d][%d,%d] center=%d,%d\n", $1,$2,$3,$4, ($1+$3)/2, ($2+$4)/2}'
|
||||
}
|
||||
|
||||
case "$CMD" in
|
||||
texts)
|
||||
dump | tr '<' '\n' | grep -oE 'text="[^"]+"' | sed 's/text=//;s/"//g' | awk '!seen[$0]++'
|
||||
;;
|
||||
|
||||
bounds)
|
||||
[ -z "$ARG" ] && { echo "需要文字参数"; exit 1; }
|
||||
centers_of "$ARG"
|
||||
;;
|
||||
|
||||
tap-text)
|
||||
[ -z "$ARG" ] && { echo "需要文字参数"; exit 1; }
|
||||
idx="${4:-1}"
|
||||
all=$(centers_of "$ARG")
|
||||
[ -z "$all" ] && { echo "界面上找不到文字:$ARG"; exit 1; }
|
||||
total=$(echo "$all" | wc -l | tr -d ' ')
|
||||
# 同一文字可能在多处出现(如底部 Tab「国际」与列表里的「国际」),
|
||||
# 默认点第 1 个;命中多个时提示,必要时用第 4 个参数指定序号。
|
||||
if [ "$total" -gt 1 ]; then
|
||||
echo "注意:${ARG} 命中 $total 处,正在点第 $idx 处(可用第 4 个参数指定序号)"
|
||||
echo "$all" | nl -w2 -s' '
|
||||
fi
|
||||
line=$(echo "$all" | sed -n "${idx}p")
|
||||
[ -z "$line" ] && { echo "序号 $idx 超出范围(共 $total 处)"; exit 1; }
|
||||
cx=${line#*center=}; cx=${cx%%,*}
|
||||
cy=${line##*,}
|
||||
# 变量必须写成 ${ARG}:紧跟中文标点时 $ARG 会被当成变量名的一部分
|
||||
echo "tap ${ARG} @ $cx,$cy"
|
||||
$A shell input tap "$cx" "$cy"
|
||||
;;
|
||||
|
||||
# 等某段文字出现 / 消失。点击前先等页面就绪很重要:
|
||||
# 加载弹窗("请稍候……")会盖住整屏并吞掉 input tap,导致点击"静默失效"。
|
||||
wait-text)
|
||||
[ -z "$ARG" ] && { echo "需要文字参数"; exit 1; }
|
||||
for _ in $(seq 1 "${4:-20}"); do
|
||||
if dump | grep -qF "text=\"$ARG\""; then echo "出现: ${ARG}"; exit 0; fi
|
||||
sleep 1
|
||||
done
|
||||
echo "超时未出现: ${ARG}"; exit 1
|
||||
;;
|
||||
|
||||
wait-gone)
|
||||
[ -z "$ARG" ] && { echo "需要文字参数"; exit 1; }
|
||||
for _ in $(seq 1 "${4:-20}"); do
|
||||
if ! dump | grep -qF "text=\"$ARG\""; then echo "已消失: ${ARG}"; exit 0; fi
|
||||
sleep 1
|
||||
done
|
||||
echo "超时仍存在: ${ARG}"; exit 1
|
||||
;;
|
||||
|
||||
shot)
|
||||
out="${ARG:-/tmp/shot.png}"
|
||||
$A exec-out screencap -p > "$out"
|
||||
echo "$out ($(wc -c < "$out") bytes)"
|
||||
;;
|
||||
|
||||
req)
|
||||
# OkHttp 日志是多行 pretty JSON,用 -v raw 拿干净文本再按关键字取上下文
|
||||
$A logcat -d -v raw | grep -B24 -A24 -- "${ARG:-POST}" | grep -E '^\s*"|POST|<-- [0-9]{3}' | tail -60
|
||||
;;
|
||||
|
||||
crash)
|
||||
n=$($A logcat -d | grep -c "FATAL EXCEPTION" || true)
|
||||
echo "FATAL EXCEPTION 次数: $n"
|
||||
if [ "$n" -gt 0 ]; then
|
||||
$A logcat -d | grep -A25 "FATAL EXCEPTION" | tail -30
|
||||
fi
|
||||
;;
|
||||
|
||||
form)
|
||||
$A logcat -d -s BaseActivity:D | grep -oE "(PHONE|TABLET)\(sw=[0-9]+dp[^)]*\)" | tail -3
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "未知命令: $CMD"; exit 1
|
||||
;;
|
||||
esac
|
||||
490
CHANGELOG.md
490
CHANGELOG.md
@@ -1,101 +1,389 @@
|
||||
# 版本变更日志 (Changelog)
|
||||
|
||||
本文档记录 AirLogistics 项目的所有重要变更。
|
||||
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
||||
|
||||
## [1.8.4] - 2024-12-02
|
||||
|
||||
### 新增 (Added)
|
||||
- 完善 Claude Code 配置,添加 Android 开发权限
|
||||
- 创建 8 个快捷命令 (/build-debug, /install, /logs 等)
|
||||
- 定制 CLAUDE.md 为 Android 项目专用开发指南
|
||||
- 添加 CONTRIBUTING.md 贡献指南文档
|
||||
- 添加 CHANGELOG.md 版本变更日志
|
||||
|
||||
### 改进 (Changed)
|
||||
- 优化首页菜单交互体验
|
||||
- 改进 UI 界面显示效果
|
||||
|
||||
### 技术栈
|
||||
- Kotlin 1.6.21
|
||||
- Android Gradle Plugin 7.2.0
|
||||
- Gradle 7.3.3
|
||||
- minSdk 24, targetSdk 30, compileSdk 31
|
||||
|
||||
---
|
||||
|
||||
## [1.8.x] - 之前版本
|
||||
|
||||
### 主要功能
|
||||
- 国内出港业务模块 (货物收运、复磅称重、转运管理、出库装机)
|
||||
- 国内进港业务模块 (舱单管理、卸机入库、出库提货、移库管理)
|
||||
- 国际出港业务模块 (国际货物收运、板箱组装、ULD容器管理)
|
||||
- 国际进港业务模块 (国际舱单管理、报文解析、理货管理)
|
||||
- 航班管理模块 (航班查询、航班统计)
|
||||
- 货物追踪模块 (货物状态追踪、运输日志)
|
||||
- 监装监卸模块 (监装监卸管理)
|
||||
- PDA功能模块 (PDA专用功能)
|
||||
- 蓝牙打印模块 (佳博打印机集成)
|
||||
|
||||
### 核心架构
|
||||
- MVVM 架构模式
|
||||
- 组件化模块设计 (11个业务模块 + 1个基础库)
|
||||
- DataBinding 双向绑定
|
||||
- Kotlin Coroutines + Flow 异步处理
|
||||
- ARouter 模块间通信
|
||||
- Retrofit + OkHttp 网络请求
|
||||
- Glide 图片加载
|
||||
- MPAndroidChart 图表展示
|
||||
|
||||
### 开发基础设施
|
||||
- BaseActivity/BaseBindingActivity 基类
|
||||
- BaseViewModel/BasePageViewModel 基类
|
||||
- CommonAdapter + BaseViewHolder 列表适配
|
||||
- PadSearchLayout/PadDataLayout 自定义组件
|
||||
- 完整的 Kotlin 扩展函数库
|
||||
- DataBinding 适配器集合
|
||||
|
||||
---
|
||||
|
||||
## 版本说明
|
||||
|
||||
### 版本号规则
|
||||
- **Major.Minor.Patch** (例: 1.8.4)
|
||||
- **Major**: 重大架构变更或不兼容的 API 修改
|
||||
- **Minor**: 新功能添加,向下兼容
|
||||
- **Patch**: Bug 修复和小的改进
|
||||
|
||||
### 变更类型
|
||||
- **Added**: 新增功能
|
||||
- **Changed**: 功能改进或变更
|
||||
- **Deprecated**: 即将废弃的功能
|
||||
- **Removed**: 已移除的功能
|
||||
- **Fixed**: Bug 修复
|
||||
- **Security**: 安全性修复
|
||||
|
||||
---
|
||||
|
||||
## 未来计划
|
||||
|
||||
### 待开发功能
|
||||
- [ ] 优化数据同步机制
|
||||
- [ ] 增强离线模式支持
|
||||
- [ ] 改进用户权限管理
|
||||
- [ ] 优化蓝牙打印稳定性
|
||||
- [ ] 添加数据导出功能
|
||||
- [ ] 性能监控和分析
|
||||
|
||||
### 技术改进
|
||||
- [ ] 升级到 Kotlin 2.0
|
||||
- [ ] 迁移到 Jetpack Compose
|
||||
- [ ] 优化构建速度
|
||||
- [ ] 完善单元测试覆盖
|
||||
- [ ] 添加 UI 自动化测试
|
||||
|
||||
---
|
||||
|
||||
**维护**: 本文档应在每次版本发布时更新。
|
||||
**负责人**: 项目维护团队
|
||||
**最后更新**: 2024-12-02
|
||||
# 版本变更日志 (Changelog)
|
||||
|
||||
本文档记录 AirLogistics 项目的所有重要变更。
|
||||
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### 变更 (Changed)
|
||||
- 【进港移库归属更正 · 2026-08-03】设计稿 11「进港移库」经确认归属**国内进港移库
|
||||
(DomImpMove,后端 8 接口齐全,Pad 首页「国内进港→进港移库」既有页面)**,此前误判为
|
||||
国际进港并按 IntExpMove 约定预接不存在的 IntImpMove 模块(原问题清单 #31 作废)。处置:
|
||||
① 删除 module_gjj 的 IntImpMove 全套前端实现(3 Activity + 3 ViewModel + Holder + 4 布局 +
|
||||
`IntImpMoveBean` + Api 4 方法 + 国际 Tab 菜单项 + 权限串/路由/双 Manifest 注册);
|
||||
② 按 phone-adapt 流程重新适配到 `GnjMoveStashListActivity`(app 模块):主列表双形态
|
||||
(Pad 布局移 layout-sw600dp 零改动;手机版 = PhoneSearchBar + 待移库/已移库 Tab
|
||||
(`DomImpMove/search`/`searchMoved` 双端点,`searchMoved` 无移库标识按端点打 `isMoved`)+
|
||||
角标(双端点 limit=1 取 total,仅手机发起)+ 筛选弹层 4 项 + 全选/已选计数 + 批量移库
|
||||
(复用平板 `move` 链路)+ 卡片长按「拍照」浮层);新增手机专属页
|
||||
`GnjMoveStashDetailActivity`(Intent 传 bean 零请求,三卡片)与
|
||||
`GnjMoveStashPhotoActivity`(先调 `detail` 回填已有照片,提交走 `modify` 最小体
|
||||
三字段规范,与平板移交编辑页同链路);`MoveStashBean` 增 `agentName`/`isMoved`/
|
||||
`actionMenuVisible`/`agentDisplay`/`orDash`;手机首页入口改挂**国内 Tab**(`GnViewModel`
|
||||
补通用 route 跳转,权限串 `GnjYiKu=AppDomImpMove` 与 Pad 一致,新路由
|
||||
`/app/GnjMoveStashListActivity`)。
|
||||
2026-08-03 内网真实数据双端验证通过(登录→国内 Tab→列表/Tab/详情/筛选/全选/长按拍照全链路
|
||||
crash=0;平板回归原行为零变化);Proxyman A/B:`wbNo`/`awbType` 过滤生效,
|
||||
`fdate`/`fno`/`spCode` 被后端静默忽略(新缺口 #32)、`searchMoved`/`detail` 出参
|
||||
`opId`/`opDate` 恒 null 且无姓名字段(#33)、`searchMoved` 无 pic 三字段(#34,前端已规避)
|
||||
|
||||
### 新增 (Added)
|
||||
- 「进港查询」5.5 寸手机端适配(设计稿 10_进港查询,06 出港查询的进港镜像;详情页与运单追踪
|
||||
复用 08/06 已适配页零新增):列表 `IntImpQueryActivity`(module_gjj,首页菜单实际跳转页;
|
||||
旧版 `GjjQueryListActivity` 路由已注释弃用)手机布局 = PhoneSearchBar(运单号+扫码+筛选)+
|
||||
三格统计(总票数/已入库/已出库,已入库/已出库为已加载数据客户端计数,`pageQueryTotal` 无分项
|
||||
出参,缺口 #30)+ 状态标签卡片(前端推断:`dlvTime` 非空=已出库绿 / `inDate` 非空=已入库橙 /
|
||||
兜底未入库灰)+ 卡片字段件数(inPc)/重量(inWeight)/代理人/特码;筛选弹层 9 项
|
||||
(航班日期起止/航班号/代理/特码/始发站/运单类型/业务类型/品名中,**全部经 api-doc 核对为
|
||||
pageQuery 真实入参**,无缺口);平板侧滑筛选面板代码判空守卫,Pad 端行为零变化
|
||||
- 「进港移库」5.5 寸手机端全新页面(设计稿 11_进港移库;**首个后端整模块缺失的适配页**:
|
||||
api-doc 确认无 IntImpMove 模块、权限体系无对应 key,缺口 #31):
|
||||
`IntImpMoveActivity`(待移库/已移库 Tab + 角标、搜索/扫码、筛选弹层 4 项(航班日期/航班号/
|
||||
特码/运单类型)、多选 + 全选 + 批量移库(ConfirmDialogModel 确认)、卡片长按浮层「拍照」)+
|
||||
`IntImpMoveDetailActivity`(运单详情三卡片:蓝头基本信息/绿头移库信息(仅已移库)/橙头异常照片,
|
||||
Intent 传 bean 零请求)+ `IntImpMovePhotoActivity`(拍照上传:图片网格复用 module_base
|
||||
`ImageSelectNewViewHolder`,提交走 pic/originalPic/picNumber 三字段规范);
|
||||
接口按 IntExpMove 同名约定预接四个(pageQuery/pageQueryTotal/move/uploadPic),权限串
|
||||
`AppIntImpMove` 预定义,后端交付后前端零改动生效(权限串下发前手机首页无入口,debug 直启验证)
|
||||
- 新增 `IntImpMoveBean`(镜像 GjcMove + 航班/照片字段 + isMoved/flightInfo/orDash 计算属性);
|
||||
`IntImpQueryBean` 新增 isQueryOutbound/isQueryStored/orDash/agentDisplay 计算属性;
|
||||
手机首页「国际」Tab 增加「进港查询」(图标 gjj_query_icon)与「进港移库」(图标复用
|
||||
gjc_yi_ku_icon)入口
|
||||
- 新增手机版资源:`bg_phone_btn_green`(绿色主按钮)、`bg_phone_card_header_orange`(橙色详情卡
|
||||
表头)、`bg_phone_photo_add`(虚线加号槽位)、色值 `phone_amber`
|
||||
|
||||
### 修复 (Fixed)
|
||||
- 【11 模块全量设计稿走查】修正 4 类差异(2026-07-31,3 个并行审计 + 逐态渲染比对):
|
||||
① 运单追踪手机竖排时间线补齐已发生节点的状态 pill(绿底标签展示该环节日志 content,
|
||||
如「放行状态:01」,超长省略,设计稿 06/08 运单追踪均有此元素);
|
||||
② 11 个页面手机端标题对齐设计稿短标题(出库交接/出港待计重/计重记录/计重明细/开始计重/
|
||||
出港运抵/出港移库/出港查询/出港货物/进港查询/进港货物),平板保持原长标题零变化;
|
||||
③ ULD 编辑表单「来源」字段移至「所在港」之后(设计稿顺序);
|
||||
④ 出港计重列表卡片(日期/航程/代理/特码)与计重记录卡片(代理)补齐字段小图标
|
||||
(复用 ic_phone_airplane/location/person/tag,与其余模块卡片风格统一)
|
||||
- 出港查询/进港查询卡片「代理人」空值兜底失效:转换器把 JSON null 归一成空串,DataBinding
|
||||
`??` 只判 null 不判空串导致 agentCode 回退永不生效;改用 Bean 计算属性 `agentDisplay`
|
||||
(isNullOrEmpty 双兜底),已同步修正 06 出港查询(`item_gjc_query.xml`)
|
||||
|
||||
### 验证 (Verified)
|
||||
- 进港查询手机端:Map Local mock 验证三格统计计数、三种状态标签、空值 "--" 兜底、筛选弹层
|
||||
9 项收缩滚动;平板端回归横排搜索条/侧滑筛选面板/底部统计栏零变化;全程崩溃数 0
|
||||
- 进港移库手机端:Map Local mock 验证双 Tab 角标、双形态卡片、单选/全选联动计数、空选拦截、
|
||||
移库确认弹框、长按拍照浮层、运单详情、拍照上传页;全程崩溃数 0(后端模块缺失,接口联调待
|
||||
后端交付,见问题清单 #31)
|
||||
- 「提取出库」5.5 寸手机端适配(设计稿 09_提取出库):列表 `IntImpPickUpDLVActivity`(module_gjj)
|
||||
手机布局 = PhoneSearchBar(运单号+扫码+筛选)+ **PhoneStatusTab 待出库/已出库**(`outState`
|
||||
为 pageQuery 文档定义入参 0/1,Tab 真实过滤 + 角标双 pageQueryTotal,抓包证实携带)+
|
||||
双形态卡片(待出库:圆形勾选框+橙标签+库位行;已出库:绿 √ 标签+出库人·出库时间行,
|
||||
`isPickedUp` = dlvTime 非空)+ 卡片内 PhoneStatBox 三列(代理人/件数/重量)+「查看详情」链接 +
|
||||
PhoneBottomBar(全选+已选 N 项+出库,仅待出库 Tab 显示,复用既有 confirmOutbound);
|
||||
筛选弹层 4 项(航班日期为 pageQuery 未定义入参预留;航班号 fno;代理人/特码下拉);
|
||||
手机端进入时清空平板默认「当天缴费日期」查全量(initPhoneExtras,平板默认值不变)
|
||||
- 手机专属新页「运单详情」`IntImpPickUpDetailActivity`:蓝头运单卡(红标签国际进港+品名大标题+
|
||||
两列 KV+库位橙标签)+ 绿头提货信息卡(柜台办理人/时间,已出库追加出库人/出库时间行),
|
||||
数据直接 Intent 传列表 bean(Serializable),零新增请求
|
||||
- `IntImpPickUpDLVBean` 新增 `isPickedUp` 计算属性;手机首页「国际」Tab 增加「提取出库」入口
|
||||
(权限 AppIntImpPickUpDLV 对应权限串,图标复用 Pad 端 gjj_chu_ku_icon)
|
||||
- 后端缺口 2 项登记问题清单 #28~#29(pageQuery 无 fdate 入参、出参无车牌/目的港/业务类型;
|
||||
outState 过滤与 dlvName=出库人语义待内网实测)
|
||||
|
||||
- 「进港仓库」5.5 寸手机端适配(设计稿 08_进港仓库,07 出港仓库的进港镜像 + 进港详情页首次适配):
|
||||
列表 `IntImpStorageUseActivity`(module_gjj,首页菜单实际跳转页;旧版 `GjjWareHouseActivity`
|
||||
路由已注释弃用)手机布局 = PhoneSearchBar(placeholder「搜索运单号/航班号」,`phoneSearchClick`
|
||||
分流:含字母→fno、其余→wbNo,实机抓包证实)+ 三格统计(总票数/未清仓/已清仓,clearNormal=0/1
|
||||
双 total)+ 卡片四操作按钮(清仓/修改库位/出库/入库,已清仓置灰)+ 卡片字段件数/重量/航班号/
|
||||
**始发站**(bean.dep,出港为目的站);筛选弹层 8 项(始发站走 fdep 入参;代理/特码/运单类型/
|
||||
业务类型/品名中 5 项为后端未定义入参,预留传参)
|
||||
- 进港详情页 `IntImpQueryDetailsActivity` + 3 Fragment(运单/仓库/库位信息)首次手机适配:
|
||||
移植 06 出港详情手机布局,报文区按设计稿 3 项(原始舱单 mftStatus/理货报告 tallyStatus/
|
||||
海关放行 command),件重区直取 detail 出参 awbPc/awbWeight/cashWeight/inPc/inWeight
|
||||
(进港自带无需求和),库位卡片优先显示中文姓名(inOpName/outOpName);
|
||||
ViewModel 增加 str/strOr/dec 取值帮助(与出港同口径)
|
||||
- 进港版 3 个操作弹框双变体化(清仓/入库/修改库位,手机底部弹层);修改库位手机版增加
|
||||
「原库位」单选且**只列未出库库位**(沿用平板「已出库不允许修改」校验,卡片级操作时全部已出库
|
||||
toast 拦截),单库位默认选中
|
||||
- 手机首页「国际」Tab 增加「进港仓库」入口(权限 AppGjjWareHouse=Constant.AuthName.GjjWareHouseActivity 对应权限串,图标复用出港仓库同款)
|
||||
- 后端缺口 3 项登记问题清单 #25~#27(pageQuery 无代理/特码/运单类型/业务类型/品名中 5 项筛选
|
||||
入参;clearNormal 分项统计待实测;detail 出参 dep/dest1/dest/unNumber/opDate 字段待实测确认)
|
||||
- 「出港仓库」5.5 寸手机端适配(设计稿 07_出港仓库,详情/运单追踪复用已适配页零新增):
|
||||
列表 `IntExpStorageUseActivity` 手机布局 = PhoneSearchBar(运单号 + 扫码 + 筛选)+ 三格统计
|
||||
(总票数/未清仓/已清仓,彩色左边条卡片;分项计数按同条件 + `clearNormal=0/1` 各调一次
|
||||
`pageQueryTotal`,仅手机形态发起)+ 状态标签卡片(已清仓绿/未清仓橙,`clearNormal=="1"` 判定);
|
||||
**卡片级四操作按钮**(清仓/修改库位/出库/入库,已清仓时清仓置灰拦截)取代平板的勾选+底部批量条,
|
||||
语义等价于「勾选该主单+全部库位」复用既有 `performClear/performModifyStorage/performOutStorage/
|
||||
performInStorage` 请求链路;卡片点击进详情(复用出港查询详情 `GjcQueryDetailsActivity` 3 Tab
|
||||
运单/仓库/库位信息,运单追踪同样复用 `LogDetailActivity`);筛选弹层 8 项按设计稿完整实现
|
||||
(航班日期/航班号/代理/特码/目的站/运单类型/业务类型/品名中,后三项为后端未定义入参,预留传参)
|
||||
- 清仓/入库/修改库位三个操作弹框(`IntExpMoveClearDialogModel`/`IntExpInStorageDialogModel`/
|
||||
`IntExpModifyStorageDialogModel`)双变体化:手机底部弹层(PhoneDataLayout)+ 平板居中弹框不变,
|
||||
弹出位置按 `DeviceUtil.isPhone()` 切换;修改库位弹框手机版新增「原库位」单选
|
||||
(卡片级操作时运单可能有多个库位,构造参数 `sourceStorageList` 传入,单库位默认选中,平板路径不受影响)
|
||||
- 出库二次确认由系统 `AlertDialog` 改为项目规范的 `ConfirmDialogModel`(平板批量出库与手机卡片出库共用)
|
||||
- 手机首页「国际」Tab 增加「出港仓库」入口(权限 AppIntExpStorageUse,图标复用 Pad 端 gjc_cang_ku_icon)
|
||||
- `GjcMaWb` 新增 `isCleared` 手机版计算属性;新增手机版资源 `bg_phone_btn_disabled`(灰底禁用按钮)
|
||||
- 后端缺口 2 项登记问题清单 #23~#24(pageQuery/pageQueryTotal 无 awbType/businessType/goodsCn
|
||||
筛选入参;pageQueryTotal 的 clearNormal 分项统计过滤待内网 A/B 实测确认)
|
||||
- 「出港查询」5.5 寸手机端适配(设计稿 06_出港查询,含列表/详情/运单追踪三页):
|
||||
列表 `GjcQueryActivity` 手机布局 = PhoneSearchBar(运单号 + 扫码 + 筛选)+ 三格统计
|
||||
(总票数/已入库/已离港,彩色左边条卡片)+ 状态标签卡片(已离港绿/已入库橙/未入库灰,
|
||||
前端推断口径:fclose 非空=已离港,否则 opDate 非空=已入库);筛选弹层 9 项(平板搜索行的
|
||||
日期起止/代理并入弹层,特码按设计稿改下拉,字典 `getSpecialCodeList(flag=1, ieFlag="")`,
|
||||
仅手机形态经 `initPhoneExtras()` 加载);详情 `GjcQueryDetailsActivity` 3 Tab 全部复用
|
||||
ViewPager2 + Fragment,手机变体以 PhoneKvItem 网格呈现「基本信息/件重信息/回执信息」,
|
||||
比平板多展示承运人/始发站/中转站/目的站/UN编号,入库件数/入库重量 = warehouseList 各批次求和;
|
||||
仓库/库位 Tab 改批次/库位卡片;「运单追踪」为公共页 `LogDetailActivity` 双变体首例——
|
||||
手机竖排流转时间线(Activity `buildVerticalSteps` 动态构建,节点绿=已达/灰=未达 + 环节时间),
|
||||
平板横向步骤条零改动,手机标题按形态显示「运单追踪」
|
||||
- 手机首页「国际」Tab 增加「出港查询」入口(权限 AppIntExpSearch,图标复用 Pad 端 gjc_query_icon)
|
||||
- `PhoneKvItem` 新增 `tagColor` 属性("green" 绿色高亮,海关放行正常态;默认 "orange"),
|
||||
且占位符 "-"/"--" 不再套高亮标签底
|
||||
- `GjcMaWb` 新增 `isQueryDeparted`/`isQueryStored`/`orDash` 手机版计算属性
|
||||
- 新增手机版资源:`ic_phone_package`(件数)、`ic_phone_tag`(特码)、`bg_phone_dot_blue/gray`
|
||||
- 后端缺口 2 项登记问题清单 #21~#22(isFclose 入参实测被 pageQuery/pageQueryTotal 静默忽略且
|
||||
无已入库/已离港分项统计出参;detail 出参无锁定状态/计费重量字段)
|
||||
|
||||
### 修复 (Fixed)
|
||||
- 提取出库手机版底部条「已选 N 项」在点全选后不更新:`checkAllClick` 只改勾选状态未同步
|
||||
`selectedCountText`(卡片单选走 FlowBus→updateCheckAllStatus 正常),补同步后实测「已选 3 项」正确
|
||||
- **`PhoneFilterPanel` 底部按钮被挤出屏幕**:面板高度 wrap_content 且内容插槽无滚动,筛选项多
|
||||
(出港查询 9 项)时「重置/确认」按钮直接超出屏幕外且无任何报错。修复:内容插槽外包
|
||||
ScrollView(weight=1),筛选项少时行为不变,超屏时内容区收缩滚动、按钮始终可见
|
||||
(已回归出港计重 4 项弹层,行为不变)
|
||||
|
||||
### 验证 (Verified)
|
||||
- 提取出库双端验证通过(2026-07-31,Proxyman Map Local mock 口径):手机端菜单入口、
|
||||
Tab 角标(双 total)、双形态卡片(含库位空值兜底)、单选/全选与「已选 N 项」联动、
|
||||
出库 ConfirmDialogModel、待出库/已出库两种详情页形态(出库人行按需显隐)、筛选弹层 4 项、
|
||||
Tab 切换请求体抓包证实 outState=1 正确携带、冷启动方向恒竖屏、崩溃 0;
|
||||
平板端回归:缴费日期默认当天/5 搜索条/全选/底部统计/确认出库与改造前一致,崩溃 0。
|
||||
**待内网补测**:outState 过滤 A/B、dlvName=出库人语义、出库真实提交链路
|
||||
- 进港仓库双端验证通过(2026-07-31,同出港仓库为 Proxyman Map Local mock 口径):
|
||||
手机端登录→国际 Tab→进港仓库菜单入口、三格统计、三种卡片形态(含空值 "--" 兜底)、
|
||||
详情 3 Tab 全字段(锁定状态转中文、库位中文姓名、时间截断、空值兜底)、四操作弹框、
|
||||
已出库库位过滤(双库位卡片原库位仅列未出库项并默认选中)、无库位/已清仓拦截、
|
||||
筛选弹层 8 项、搜索分流实机抓包证实(输入 MU1111 → 请求体 fno="MU1111"、wbNo=null)、
|
||||
冷启动方向恒竖屏、崩溃 0;平板端回归:5 搜索条/全选/底部统计/4 批量按钮与改造前一致,崩溃 0。
|
||||
**待内网补测**:clearNormal 分项统计 A/B、detail 推断字段(dep/dest1/dest/unNumber/opDate)、
|
||||
四操作真实提交链路
|
||||
- 出港仓库双端验证通过(2026-07-31,开发机不在内网 → 全程 Proxyman Map Local mock 口径):
|
||||
手机端登录→国际 Tab→出港仓库菜单入口、三格统计渲染、三种卡片形态(已清仓置灰清仓/未清仓/
|
||||
全空字段 "--" 兜底)、四操作按钮(清仓/入库底部弹层、修改库位弹层含原库位默认选中、
|
||||
出库 ConfirmDialogModel 文案带运单号与库位数、无库位卡片 toast 拦截、已清仓清仓点击拦截)、
|
||||
筛选弹层 8 项滚动 + 底部按钮可见、卡片点击进详情(Proxyman flow 证实 maWbId 正确传参且
|
||||
detail mock 命中)、详情右上运单追踪跳转、冷启动方向恒竖屏(ROTATION_0×3 采样)、崩溃 0;
|
||||
平板端同页面回归:5 搜索条/全选/底部统计/4 批量按钮与改造前一致,崩溃 0。
|
||||
**待内网补测**:clearNormal 分项统计 A/B、agentCode/spCode/dest 筛选实测、四操作真实提交链路
|
||||
- 出港查询双端实机验证通过(2026-07-31,内网真实数据 2026-06-09 共 14 票):
|
||||
手机端登录→国际 Tab→出港查询菜单入口、列表加载/状态标签/三格统计(10+4=14 与卡片一致)、
|
||||
筛选弹层(日期滚轮、特码/代理下拉真实取数、重置、确认后重新查询)、运单号搜索、扫码拉起、
|
||||
详情 3 Tab(含仓库批次卡与库位空态)、运单追踪时间线、冷启动方向恒竖屏(mRotation=0×8 采样)、
|
||||
全程 FATAL 0;平板端回归:列表 5 搜索条 + 底部统计 + 侧滑筛选面板 + 详情表格样式均与改造前一致
|
||||
- 「出港运抵」5.5 寸手机端适配(主列表 + 3 个手机专属新页,设计稿 04_出港运抵):
|
||||
主列表 `IntExpArriveActivity` 新增「待运抵/已运抵」Tab(客户端按 declareStatus 是否为空拆分数据集,
|
||||
接口无此查询参数)+ 卡片长按浮层(待运抵→补充、已运抵→重置+回执,纯 UI 长按)+ 底部全选/已选/申报
|
||||
(仅待运抵 Tab 显示,选中范围独立于 Pad 全量选择逻辑);新增「分单明细」`IntExpArriveSubOrderActivity`
|
||||
(数据直接复用主列表已加载的 haWbList,零新增请求,长按单条重置 + 底部批量申报);新增「回执内容」
|
||||
`IntExpArriveReceiptActivity`(只读页,复用 haWbList[].response + arrivalOpDate);新增「补充信息」
|
||||
`IntExpArriveSupplementActivity`(参照 Pad 端国际进港「收发货人信息」批量应用模式,对整个运单的
|
||||
haWbList 批量应用同一份表单,后端接口不存在,已实机确认 404,见问题清单 #19)
|
||||
- 手机首页「国际」Tab 增加「出港运抵」入口
|
||||
- `GjcMaWb`/`GjcHaWb` 新增 `isPending`/`declareTagColor`/`haWbTagColor`/`arrivalOpDateText` 等
|
||||
手机版专属计算属性;`GjcHaWb` 补充 `Serializable` 实现(供 Intent 跨页传递分单列表)
|
||||
- 新增手机版资源:`bg_phone_tag_indigo`/`bg_phone_tag_amber`(申报状态三色标签)、
|
||||
`bg_phone_fab_purple`/`bg_phone_fab_amber`/`bg_phone_fab_green`(长按浮层圆形操作按钮)
|
||||
- 后端缺口 2 项登记问题清单 #19~#20(分单补充信息保存接口不存在、Tab 无申报状态查询参数)
|
||||
- 「出港运抵」对照设计稿 review 修正:状态重置弹框手机端改为设计稿底部弹层
|
||||
(**弹框适配首个先例**:`dialog_int_exp_arrive_reset.xml` 同名布局双变体,Pad 版移至
|
||||
`layout-sw600dp/`,`IntExpArriveResetDialogModel` 构造参数按 `DeviceUtil.isPhone()` 切
|
||||
`DIALOG_TYPE_BOTTOM`/`CENTER`);主列表补空态(图标 + 文案按 Tab 切换「暂无待/已运抵的运单」);
|
||||
回执页补空态兜底「暂无回执内容」(单分单入口常无回执,避免纯白屏);分单明细长按浮层按设计稿
|
||||
补「回执」按钮(单分单跳回执页);分单页底部统计加分母「已选 N / M 项」;Tab 角标初始显示 0
|
||||
|
||||
### 修复 (Fixed)
|
||||
- **RecyclerView + Tab 过滤新坑**:给 item 根节点加 `visibility="@{...}"` 切 Tab 会在列表里
|
||||
留下空白——RecyclerView/LinearLayoutManager 不像普通 ViewGroup 那样让 GONE 子项自动塌陷为
|
||||
0 高度。改为在 ViewModel 数据层维护完整列表,Tab 切换时把过滤后的子集整体 `refresh()` 给
|
||||
adapter(已实机截图验证空白消失、卡片数与角标一致)
|
||||
- **Serializable Intent + @Transient 崩溃新坑**:`GjcMaWb`/`GjcHaWb` 的 `checked` 等 UI 状态
|
||||
字段标了 `@Transient`(避免 Gson 序列化 ObservableBoolean),但这同时是 JVM `transient`
|
||||
修饰符,Java 序列化(Intent 传递机制)会跳过其恢复,反序列化后为 null,切换选中状态时
|
||||
NullPointerException 崩溃。修复:接收方 `initOnCreated` 对列表 `.map { it.copy() }` 重新
|
||||
触发类体初始化补上(已实机崩溃复现并验证修复)
|
||||
- `PhoneStatusTab`/`PhoneFilterPanel` 补上遗漏的 `dispatchSaveInstanceState`/
|
||||
`dispatchRestoreInstanceState` 屏蔽覆写(其余 5 个手机版复合组件均已重写,这两个是历史遗漏;
|
||||
同页多实例时子控件状态可能按 id 跨实例串档)
|
||||
|
||||
### 验证 (Verified)
|
||||
- 出港运抵双端实机验证通过:手机端 Tab 切换(角标/卡片双向正确)/全选/单选/长按菜单/查看分单
|
||||
/分单明细全选与长按重置/补充信息(真实数据回填+下拉字典+提交请求体完整+后端404如实呈现)/
|
||||
回执页 全链路 crash=0、方向无闪屏;平板端布局与交互(4 搜索条+分单内嵌展开+底部 5 元素操作栏)
|
||||
与改造前逐项一致
|
||||
- 出港运抵 review 修正双端实机验证通过:手机端重置底部弹层(长按→重置→下拉展开选项)/主列表空态
|
||||
/回执页空态/分单浮层重置+回执双按钮/「已选 0 / 2 项」分母 全部与设计稿一致且 crash=0;
|
||||
平板端回归:列表、批量「状态重置」弹框仍为居中 Pad 样式,行为与改造前一致
|
||||
- 「出港计重」5.5 寸手机端适配(首个全链路 4+1 页模块,设计稿 03_出港计重):
|
||||
待计重列表(三格统计 总票数/待计重/计重中 + 卡片状态标签 未计重/计重中 + 卡片内单票「提前运抵」+
|
||||
FAB 进计重记录 + 筛选弹层 通道号/承运人/运单类型/代理人)、开始计重(头部预配统计卡 + PhoneFormRow 表单,
|
||||
托盘数量/托盘自重按设计稿手输)、计重记录(三格统计 + 卡片地磅图标进明细/「查看详情」进运单详情 +
|
||||
筛选弹层 入库日期起止/代理人/特码/目的站/计重人)、计重明细(汇总卡含托盘总重 + 记录卡「修改」→
|
||||
全屏「修改记录」覆层,复用平板 isEditMode 编辑链路,保存后留在本页)、手机专属新页「运单详情」
|
||||
`GjcWeighingWbDetailActivity`(12 字段只读网格);手机首页「国际」Tab 增「出港计重」入口
|
||||
- 新增手机版公共资源:`bg_phone_btn_weak`(浅蓝弱按钮)、`bg_phone_card_top_accent`(卡片顶部主色描边条)、
|
||||
`ic_phone_clipboard`、`ic_phone_weight`
|
||||
- `GjcMaWb` 预留 `palletNumber`/`carWeight` 字段、`GjcCheckInRecord` 增 `carWeightStr` 双向绑定属性
|
||||
(均为手机版专属,平板请求体零变化);后端缺口 7 项登记问题清单 #12~#18
|
||||
(checkIn 过滤不生效、主列表筛选三入参、入库日期范围、通道号出参、计重人姓名、arriveFlag 不回填、
|
||||
托盘数量/自重入参)
|
||||
|
||||
### 修复 (Fixed)
|
||||
- 出港计重手机版搜索分流按实测修正:`likeNo` 并非任意模糊(按 8 位 no 匹配),
|
||||
11 位全号改走 `wbNo` 精确查询,含字母走 `fno`
|
||||
|
||||
### 验证 (Verified)
|
||||
- 出港计重双端实机验证通过:手机端 待计重列表/筛选/开始计重(必填校验)/计重记录/记录筛选/运单详情/
|
||||
计重明细/修改覆层保存/单票提前运抵 全链路 crash=0、方向无闪屏、请求体新参数全部正确携带;
|
||||
平板端 待计重/计重记录/开始计重 三页回归无变化(横屏、5 搜索条、日期默认今天、地磅区/托盘车号/浮动按钮组均在)
|
||||
- 「ULD管理」5.5 寸手机端适配(app 模块首个适配页):列表卡片(三色状态标签)+ 筛选弹层(入库时间/进港航班号/所属航司/来源)+
|
||||
右下角浮动新增 + 底部全选/批量删除(循环调用单条删除接口)+ 一套 `activity_uld_edit` 手机布局覆盖 新增/修改/详情 三态
|
||||
(详情态右上铅笔进入修改、状态底部选择面板、所在港默认 HFE、必填校验仅手机生效)
|
||||
- 新增手机版公共组件 `PhoneFormRow`(行式表单项,INPUT/SELECT/DATE/TEXT 四形态,SELECT 内置底部选择面板);
|
||||
`PhoneBottomBar` 增加 `actionDanger` 危险按钮样式;新增 `bg_phone_fab`、`bg_phone_btn_danger`、`bg_phone_tag_red_light`、
|
||||
`ic_phone_trash`、`ic_phone_edit` 等资源
|
||||
- `ULDBean` 补解析接口既有出参 `ifNo`/`efNo`/`checkInDate`/`checkOutDate` 并预留 `source`;后端缺口 5 项
|
||||
(状态三态、来源字段、筛选入参、批量删除接口、字段语义)登记问题清单 #7~#11
|
||||
|
||||
### 验证 (Verified)
|
||||
- ULD管理双端实机验证通过:手机端列表/筛选/详情/修改/新增/单选全选/批删确认全链路 crash=0、方向无闪屏;
|
||||
平板端列表与详情回归无变化
|
||||
|
||||
### 修复 (Fixed)
|
||||
- 出库交接手机版「待/已交接」Tab 与角标计数的过滤参数名修正:`hoState` → `handoverState`
|
||||
(以 api-doc 接口文档为准;此前误传 `hoState` 被后端静默忽略,两个 Tab 返回同一批数据)。
|
||||
2026-07-29 内网实机验证通过:fdate=2026-07-16 下待交接/已交接 Tab 分别精确返回对应板箱,与后端直调结果一致。
|
||||
验证中发现 `IntExpOutHandover/pageQueryTotal` 出参恒 0(后端缺陷,致 Tab 角标与平板底部统计为 0,已记录问题清单 #6)
|
||||
|
||||
### 新增 (Added)
|
||||
- 新增《后端接口对接问题清单》(`documents/后端接口对接问题清单.xlsx`):手机端适配走查发现的接口缺口
|
||||
(移库列表缺航班字段、缺操作人姓名、`opId/opdate/businessType` 不回填、出库交接查询缺「交接人」入参),
|
||||
按模块/页面/接口定义/接口地址索引,作为与后端对接的持续维护文档
|
||||
- phone-adapt skill 新增 `references/api-doc.md`:api-doc MCP 连接与逐接口核对流程,接口对齐从「问用户」升级为「先查接口文档」
|
||||
|
||||
---
|
||||
|
||||
## [1.9.0] - 2026-07-29
|
||||
|
||||
### 新增 (Added)
|
||||
- 5.5 寸手持端双形态适配框架:同名布局 + 资源限定符(`layout/` 手机、`layout-sw600dp/` 平板)自动切换,业务逻辑(ViewModel/ViewHolder)零改动
|
||||
- 新增 `DeviceUtil` 设备形态判定工具,及手机版公共组件库(`PhoneSearchBar`、`PhoneStatusTab`、`PhoneDataLayout`、`PhoneFilterPanel`、`PhoneBottomBar`、`PhoneStatBox`、`PhoneKvItem`)
|
||||
- 完成「国际出港移库」「国际出港出库交接」两个页面的手机端适配,含各自的详情页(`IntExpMoveDetailActivity`、`IntExpOutHandoverDetailActivity`)
|
||||
- 手机端首页菜单接入「出港移库」「出库交接」入口
|
||||
|
||||
### 修复 (Fixed)
|
||||
- 修复手机端进入页面时「先横屏、约 1 秒后转竖屏」的闪屏问题:Manifest 统一改为 `android:screenOrientation="unspecified"`(全项目 192 处),由 `BaseActivity.applyDeviceOrientation()` 按设备形态在运行时锁定方向,避免系统建窗口时用错误方向
|
||||
- 修复平板端因中间方案(`@integer/screen_orientation` 资源限定符)引入的回归:先竖屏再转横屏,且交互后 UI 整体放大约 1.6 倍(AutoSize 误选平板竖屏基准所致);验证系统解析 Manifest 属性不套用设备限定符,恢复为 `unspecified` 方案后两端方向与缩放均恢复正常
|
||||
- AutoSize 增加手机竖屏专用设计基准 390×844dp,避免手机端沿用平板竖屏 720dp 基准导致 UI 压缩
|
||||
|
||||
### 技术栈
|
||||
- Kotlin 1.6.21
|
||||
- Android Gradle Plugin 7.2.0
|
||||
- Gradle 7.3.3
|
||||
- minSdk 24, targetSdk 30, compileSdk 31
|
||||
|
||||
---
|
||||
|
||||
## [1.8.4] - 2024-12-02
|
||||
|
||||
### 新增 (Added)
|
||||
- 完善 Claude Code 配置,添加 Android 开发权限
|
||||
- 创建 8 个快捷命令 (/build-debug, /install, /logs 等)
|
||||
- 定制 CLAUDE.md 为 Android 项目专用开发指南
|
||||
- 添加 CONTRIBUTING.md 贡献指南文档
|
||||
- 添加 CHANGELOG.md 版本变更日志
|
||||
|
||||
### 改进 (Changed)
|
||||
- 优化首页菜单交互体验
|
||||
- 改进 UI 界面显示效果
|
||||
|
||||
### 技术栈
|
||||
- Kotlin 1.6.21
|
||||
- Android Gradle Plugin 7.2.0
|
||||
- Gradle 7.3.3
|
||||
- minSdk 24, targetSdk 30, compileSdk 31
|
||||
|
||||
---
|
||||
|
||||
## [1.8.x] - 之前版本
|
||||
|
||||
### 主要功能
|
||||
- 国内出港业务模块 (货物收运、复磅称重、转运管理、出库装机)
|
||||
- 国内进港业务模块 (舱单管理、卸机入库、出库提货、移库管理)
|
||||
- 国际出港业务模块 (国际货物收运、板箱组装、ULD容器管理)
|
||||
- 国际进港业务模块 (国际舱单管理、报文解析、理货管理)
|
||||
- 航班管理模块 (航班查询、航班统计)
|
||||
- 货物追踪模块 (货物状态追踪、运输日志)
|
||||
- 监装监卸模块 (监装监卸管理)
|
||||
- PDA功能模块 (PDA专用功能)
|
||||
- 蓝牙打印模块 (佳博打印机集成)
|
||||
|
||||
### 核心架构
|
||||
- MVVM 架构模式
|
||||
- 组件化模块设计 (11个业务模块 + 1个基础库)
|
||||
- DataBinding 双向绑定
|
||||
- Kotlin Coroutines + Flow 异步处理
|
||||
- ARouter 模块间通信
|
||||
- Retrofit + OkHttp 网络请求
|
||||
- Glide 图片加载
|
||||
- MPAndroidChart 图表展示
|
||||
|
||||
### 开发基础设施
|
||||
- BaseActivity/BaseBindingActivity 基类
|
||||
- BaseViewModel/BasePageViewModel 基类
|
||||
- CommonAdapter + BaseViewHolder 列表适配
|
||||
- PadSearchLayout/PadDataLayout 自定义组件
|
||||
- 完整的 Kotlin 扩展函数库
|
||||
- DataBinding 适配器集合
|
||||
|
||||
---
|
||||
|
||||
## 版本说明
|
||||
|
||||
### 版本号规则
|
||||
- **Major.Minor.Patch** (例: 1.8.4)
|
||||
- **Major**: 重大架构变更或不兼容的 API 修改
|
||||
- **Minor**: 新功能添加,向下兼容
|
||||
- **Patch**: Bug 修复和小的改进
|
||||
|
||||
### 变更类型
|
||||
- **Added**: 新增功能
|
||||
- **Changed**: 功能改进或变更
|
||||
- **Deprecated**: 即将废弃的功能
|
||||
- **Removed**: 已移除的功能
|
||||
- **Fixed**: Bug 修复
|
||||
- **Security**: 安全性修复
|
||||
|
||||
---
|
||||
|
||||
## 未来计划
|
||||
|
||||
### 待开发功能
|
||||
- [ ] 优化数据同步机制
|
||||
- [ ] 增强离线模式支持
|
||||
- [ ] 改进用户权限管理
|
||||
- [ ] 优化蓝牙打印稳定性
|
||||
- [ ] 添加数据导出功能
|
||||
- [ ] 性能监控和分析
|
||||
|
||||
### 技术改进
|
||||
- [ ] 升级到 Kotlin 2.0
|
||||
- [ ] 迁移到 Jetpack Compose
|
||||
- [ ] 优化构建速度
|
||||
- [ ] 完善单元测试覆盖
|
||||
- [ ] 添加 UI 自动化测试
|
||||
|
||||
---
|
||||
|
||||
**维护**: 本文档应在每次版本发布时更新。
|
||||
**负责人**: 项目维护团队
|
||||
**最后更新**: 2026-07-29
|
||||
|
||||
418
CLAUDE.md
418
CLAUDE.md
@@ -839,6 +839,387 @@ adb logcat | grep "com.lukouguoji.aerologic" # 日志
|
||||
|
||||
---
|
||||
|
||||
## 5.5 寸手持端(手机版)双形态适配规范
|
||||
|
||||
项目需同时支持 **10 寸平板横屏** 与 **5.5 寸手机竖屏**。核心原则:**业务逻辑层完全复用,只重写布局**。
|
||||
|
||||
### 核心机制:布局按资源限定符自动切换
|
||||
|
||||
```
|
||||
res/layout-sw600dp/activity_xxx.xml ← 平板(最小宽度 >= 600dp)
|
||||
res/layout/activity_xxx.xml ← 手机(兜底,5.5 寸约 360~411dp)
|
||||
```
|
||||
|
||||
两个变体**同名**,DataBinding 会生成同一个 Binding 基类 + 两个变体实现:
|
||||
|
||||
```
|
||||
ActivityXxxBinding.java 公共基类(Activity 中引用的就是它)
|
||||
ActivityXxxBindingImpl.java 手机变体
|
||||
ActivityXxxBindingSw600dpImpl.java 平板变体
|
||||
```
|
||||
|
||||
因此 **Activity / ViewModel / ViewHolder 的 `layoutId()`、`itemLayoutId` 全部保持原样,无需任何设备判断**。
|
||||
|
||||
### ⛔ 严禁:用「不同文件名 + 代码判断」切换布局
|
||||
|
||||
```kotlin
|
||||
// ❌ 错误:会生成两个不同的 Binding 类
|
||||
override fun layoutId() =
|
||||
if (DeviceUtil.isPhone()) R.layout.activity_xxx_phone else R.layout.activity_xxx
|
||||
```
|
||||
|
||||
`activity_xxx_phone.xml` 生成的是 `ActivityXxxPhoneBinding`,而 `binding` 字段声明的类型是 `ActivityXxxBinding`。
|
||||
泛型擦除导致**编译期不报错**,但一进入该页面就 `ClassCastException` 崩溃。**必须用同名文件 + 资源限定符。**
|
||||
|
||||
### 布局变体的强制约束
|
||||
|
||||
| 约束 | 说明 |
|
||||
|------|------|
|
||||
| **DataBinding 变量必须完全一致** | 两个变体 `<data>` 中的 `<variable>` 名称与类型必须相同,否则编译失败 |
|
||||
| **共用控件 id 必须双方都有** | Activity / ViewHolder 中引用的 id(如 `srl`/`rv`/`checkIcon`/`iv_icon`)两个变体都要保留 |
|
||||
| **单侧独有的 id 自动变 `@Nullable`** | 只在一个变体存在的 id,Binding 基类中会是可空字段,Kotlin 侧强制 null 检查(编译器自动保障,不用担心漏改) |
|
||||
|
||||
### 已改造的基础设施
|
||||
|
||||
| 文件 | 作用 |
|
||||
|------|------|
|
||||
| `module_base/.../util/DeviceUtil.kt` | 设备形态判定。按 `smallestScreenWidthDp >= 600` 区分平板/手机;读 `Resources.getSystem()` 以绕开 AutoSize 对 density 的改写,保证判定稳定 |
|
||||
| `module_base/BaseActivity.kt` | `applyDeviceOrientation()` 按形态锁方向(手机竖屏 / 平板横屏)。Manifest 统一写 `unspecified` 配合它使用。子类覆写 `lockOrientationByDevice()` 返回 false 可跳过 |
|
||||
| `module_base/base/BaseBindingActivity.kt` | 注释固化变体约定(`layoutId()` 不做设备分支) |
|
||||
| `module_base/MyApplication.kt` | **AutoSize 增加手机竖屏基准 390×844**(见下节) |
|
||||
| `module_base/res/values/colors.xml` | 手机端配色 `phone_*` 系列 |
|
||||
| `module_base/res/drawable/bg_phone_*.xml` | 手机端卡片、搜索框、状态标签、主按钮背景 |
|
||||
| `module_base/res/layout/title_tool_bar.xml` | 手机版公共标题栏(平板版已移至 `layout-sw600dp/`) |
|
||||
|
||||
`DeviceUtil` 用法:
|
||||
|
||||
```kotlin
|
||||
DeviceUtil.isPhone() // 是否手机
|
||||
DeviceUtil.isTablet() // 是否平板
|
||||
DeviceUtil.describe() // "PHONE(sw=411dp, force=AUTO)",日志排查用
|
||||
DeviceUtil.forceMode = DeviceUtil.ForceMode.PHONE // 调试:在平板上强制走手机布局
|
||||
```
|
||||
|
||||
> `DeviceUtil.layout(padId, phoneId)` 仅用于**极少数**确实需要代码级判断的场景(如非 DataBinding 的布局)。
|
||||
> **常规页面一律用资源限定符,不要调用它。**
|
||||
|
||||
### ⚠️ 屏幕方向:Manifest 一律写 `unspecified`,由 `BaseActivity` 按形态锁定
|
||||
|
||||
**症状(改造前)**:手机上打开页面(开屏页最明显)先显示横屏,约 1 秒后才转竖屏,伴随 Activity 重建。
|
||||
|
||||
**原因**:`android:screenOrientation` 由系统在**创建 Activity 窗口时**生效,此时业务代码还没执行。
|
||||
Manifest 写死 `userLandscape` 时,即使 `BaseActivity` 在 `super.onCreate` 之前
|
||||
`setRequestedOrientation(PORTRAIT)`,也只能在横屏窗口已建好之后再纠正 → 必然闪一下。
|
||||
|
||||
**做法**:Manifest 统一写 `unspecified`,让系统按设备**当前物理方向**建窗口
|
||||
(平板横屏摆放 → 横屏起,手机 → 竖屏起,两端起始方向本就正确),
|
||||
`BaseActivity.applyDeviceOrientation()` 再按形态锁定,不产生二次纠正:
|
||||
|
||||
```xml
|
||||
<activity android:name="…"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:screenOrientation="unspecified" />
|
||||
```
|
||||
|
||||
全项目 192 处已统一(`module_p` 等 24 处本就是 `portrait` 的 PDA 页面保持不变)。
|
||||
|
||||
#### ⛔ 别用 `@integer/screen_orientation` 这类资源引用(已验证行不通)
|
||||
|
||||
一度改成 `android:screenOrientation="@integer/screen_orientation"` + `values/`=portrait、
|
||||
`values-sw600dp/`=userLandscape,**看似优雅,实测是错的**:
|
||||
|
||||
系统解析 Manifest 属性时**用的是默认配置,不套用设备限定符**,所以 `values-sw600dp/` 永远不会被选中,
|
||||
所有 Activity 实际都拿到了 `values/` 的竖屏值。后果是平板端:
|
||||
|
||||
1. 先竖屏、随后被 `BaseActivity` 纠正成横屏 —— 反而把闪屏问题搬到了平板上
|
||||
2. **AutoSize 在竖屏配置下选了平板竖屏基准 720dp**(density=1280/720≈1.78,正常应为 1280/1152≈1.11)
|
||||
→ 交互几次后整个 UI 放大约 1.6 倍
|
||||
|
||||
> 验证方法(可复现):把 `values/` 与 `values-sw600dp/` 的值**互换**,并临时让
|
||||
> `lockOrientationByDevice()` 返回 false(排除运行时纠正),装到平板上看首屏方向。
|
||||
> 结果是平板取了 `values/` 的值 → 证明限定符被忽略。
|
||||
|
||||
### ⚠️ AutoSize 设计基准必须区分手机(易漏且后果严重)
|
||||
|
||||
项目使用 AndroidAutoSize,`MyApplication.initAutoSizeConfig()` 按方向+设备切换设计尺寸:
|
||||
|
||||
| 场景 | 设计基准 |
|
||||
|------|----------|
|
||||
| 横屏(平板) | 1152 × 720 dp |
|
||||
| 竖屏 + 手机 | **390 × 844 dp**(对应 5.5 寸设计稿画布) |
|
||||
| 竖屏 + 平板 | 720 × 1280 dp |
|
||||
| PictureSelector | 360 × 480 dp |
|
||||
|
||||
**若手机沿用平板竖屏的 720dp 基准,手机上所有 dp 会被压缩约一半,布局全部错位。**
|
||||
|
||||
### 手机版 UI 规范(依据 `documents/5.5寸手持端设计文件/` 设计稿)
|
||||
|
||||
| 部位 | 规范 |
|
||||
|------|------|
|
||||
| 画布基准 | 390 × 844 dp |
|
||||
| 顶栏 | 高 48dp,`@color/phone_primary`(#2563EB),左侧「‹ 返回」,标题居中 18sp 加粗 |
|
||||
| 页面背景 | `@color/phone_bg` (#F3F4F6) |
|
||||
| 列表卡片 | `@drawable/bg_phone_card`,圆角 12dp,`marginHorizontal=16dp`,`marginTop=12dp`,`padding=14dp` |
|
||||
| 卡片头行 | 圆形勾选框 20dp(`radiobtn_checked_style`/`radiobtn_unchecked_style`)+ 运单号 15sp 加粗 + 右侧状态标签 |
|
||||
| 卡片字段 | 两列网格,13sp,`@color/phone_text_body`,行距 8dp |
|
||||
| 状态标签 | `bg_phone_tag_green`(已完成)/ `bg_phone_tag_gray`(未完成),11sp |
|
||||
| 搜索行 | 搜索框 40dp 高 + `bg_phone_search`,内含放大镜 + 输入 + 扫码;右侧筛选按钮 40dp |
|
||||
| 状态 Tab | 高 42dp,选中文字 `phone_primary` + 底部 28×3dp 指示条 |
|
||||
| 底部操作条 | 白底 + `elevation=8dp`,全选 + 统计(13sp/11sp 两行)+ 主按钮 `bg_phone_btn_primary` |
|
||||
| 筛选弹层 | `FrameLayout` 遮罩 `#80000000` + 底部白色面板,重置 / 确定各占一半 |
|
||||
|
||||
### 手机版页面改造步骤
|
||||
|
||||
1. **移动平板布局**:`git mv res/layout/activity_xxx.xml res/layout-sw600dp/activity_xxx.xml`(item 布局同理)
|
||||
2. **新建手机布局**:在 `res/layout/` 下创建**同名**文件,保持相同 `<variable>` 与共用控件 id
|
||||
3. **Kotlin 侧不动**:`layoutId()`、`itemLayoutId`、ViewHolder 全部保持原样
|
||||
4. **纯 UI 交互**(Tab 切换、弹层显隐)写在 Activity,不污染 ViewModel;状态过滤优先复用 ViewModel 既有字段
|
||||
5. 手机布局顶部 `<include layout="@layout/title_tool_bar" />` 不变(变体自动切换)
|
||||
6. **弹框(BaseDialogModel)同样要适配**:dialog 布局照样走同名双变体(Pad 版移 `layout-sw600dp/`、
|
||||
手机版在 `layout/` 写设计稿的底部弹层样式),弹出位置在构造参数按形态切换:
|
||||
`BaseDialogModel<XxxBinding>(if (DeviceUtil.isPhone()) DIALOG_TYPE_BOTTOM else DIALOG_TYPE_CENTER)`
|
||||
(参考 `IntExpArriveResetDialogModel` + `dialog_int_exp_arrive_reset.xml` 双变体)。
|
||||
页面适配完要**逐个排查该页所有 DialogModel**,漏掉的会在手机上弹出 Pad 居中样式
|
||||
7. **回到手机端首页加菜单入口**(最易遗忘,见下节)——不加的话页面在手机上根本进不去
|
||||
|
||||
### 手机端首页菜单入口(适配完必须加,否则没入口)
|
||||
|
||||
手机端首页是 `module_p` 的 `PDAEnterActivity`(手持机首页),底部四个 Tab 对应:
|
||||
|
||||
| Tab | ViewModel | 位置 |
|
||||
|-----|-----------|------|
|
||||
| 国内 | `GnViewModel` | `module_p/.../ui/enter/gn/` |
|
||||
| 国际 | `GjViewModel` | `module_p/.../ui/enter/gj/` |
|
||||
|
||||
**菜单项是手写清单,不是接口下发**。权限机制只做过滤:登录返回的权限串存在
|
||||
`SharedPreferenceUtil.getString(Constant.Share.authList)`(一个逗号分隔的长字符串),
|
||||
菜单项按 `authList.contains(bean.key)` 决定显不显示。
|
||||
|
||||
所以「适配完页面但手机首页看不到入口」的原因通常**不是没权限,而是清单里没加**。
|
||||
排查顺序:先确认权限串里有对应 key,再去看清单。
|
||||
|
||||
```bash
|
||||
# 看当前登录账号实际拥有的权限串
|
||||
adb -s <serial> shell "run-as com.lukouguoji.aerologic \
|
||||
cat /data/data/com.lukouguoji.aerologic/shared_prefs/data.xml" | tr ',' '\n' | grep -oE "App[A-Za-z]+" | sort -u
|
||||
```
|
||||
|
||||
加一项就是加一行 —— `ActionBean` 的第 4 个参数填 ARouter 路由即可,无需写 when 分支
|
||||
(`module_p` 只依赖 `module_base`,看不到 `module_gjc` 等业务模块,跨模块只能走路由):
|
||||
|
||||
```kotlin
|
||||
ActionBean(
|
||||
"出港移库",
|
||||
R.drawable.gjc_yi_ku_icon, // 图标复用 Pad 端同款
|
||||
Constant.AuthName.GjcYiKuListActivity, // 权限串,与 Pad 端菜单保持一致
|
||||
ARouterConstants.ACTIVITY_URL_INT_EXP_MOVE // 有 route 就走通用跳转
|
||||
),
|
||||
```
|
||||
|
||||
两点注意:
|
||||
|
||||
- **权限串与路由都要和 Pad 端菜单对齐**:以 `app/.../HomeFragment.kt` 里该权限对应的
|
||||
`ARouter.build(...)` 为准。同一业务常有新旧两个 Activity(如移库的 `GjcYiKuListActivity`
|
||||
与 `IntExpMoveActivity`),Pad 菜单实际跳的才是要适配的那个
|
||||
- **图标**:`module_p` 看不到 `module_gjc` 的资源,把 Pad 图标 PNG 复制一份到
|
||||
`module_p/src/main/res/drawable-xxhdpi/`(该模块既有 4 个图标就是这么放的,同名不同密度桶可共存)
|
||||
|
||||
**示例:状态 Tab 零 ViewModel 改动**(`IntExpMoveActivity` 已实现)
|
||||
|
||||
```kotlin
|
||||
// Activity 中新增纯 UI 方法,写入 ViewModel 既有的 moveState 字段("" 全部 / "0" 未移库 / "1" 已移库)
|
||||
fun switchTab(state: String) {
|
||||
if (viewModel.moveState.value == state) return
|
||||
viewModel.moveState.value = state
|
||||
viewModel.searchClick() // 复用原有查询链路
|
||||
}
|
||||
```
|
||||
|
||||
```xml
|
||||
<!-- XML 中判等要把字面量放前面,避免 LiveData 为 null 时崩溃 -->
|
||||
android:textColor="@{`0`.equals(viewModel.moveState) ? @color/phone_primary : @color/phone_text_body}"
|
||||
```
|
||||
|
||||
### 手机版公共组件库(`module_base/.../ui/weight/phone/`)
|
||||
|
||||
7 个组件覆盖设计稿中反复出现的部件,**新页面优先复用,不要重写 XML**。属性均为无命名空间写法,
|
||||
DataBinding 适配器统一放在 `PhoneWidgetKtx.kt`(同名属性按控件类型解析,与平板端 Pad* 组件互不冲突)。
|
||||
|
||||
| 组件 | 用途 | 关键属性 |
|
||||
|------|------|----------|
|
||||
| `PhoneSearchBar` | 顶部搜索行:搜索框 + 扫码 + 圆形筛选按钮 | `hint`、`value`(双向)、`showScan`、`showFilter`、`upperCase`、`setOnScanClickListener`、`setOnFilterClickListener`、`setRefreshCallBack` |
|
||||
| `PhoneStatusTab` | 状态 Tab 条(文字 + 角标,选中态下划线) | `tabs`(List\<KeyValue\>)、`counts`(List\<String\>)、`value`(双向)、`setOnTabChanged` |
|
||||
| `PhoneDataLayout` | 表单/筛选项,`PhoneDataLayoutType` = `INPUT`/`DATE`/`SPINNER` | `title`、`hint`、`type`、`value`(双向)、`list`、`required`、`enable`、`setRefreshCallBack` |
|
||||
| `PhoneFilterPanel` | 底部筛选弹层(遮罩 + 面板 + 重置/确认) | `title`、`panelVisible`、`setOnResetClick`、`setOnConfirmClick`、`setOnDismissClick` |
|
||||
| `PhoneBottomBar` | 底部操作条:全选 + 已选统计 + 主按钮 | `allChecked`、`countText`、`actionText`、`setOnAllCheckClick`、`setOnActionClick` |
|
||||
| `PhoneStatBox` | 卡片内 2~4 列统计框(未传 label 的列自动隐藏) | `label1..4`、`value1..4` |
|
||||
| `PhoneKvItem` | 详情页字段项(灰标签 + 数值),`tag=true` 转高亮标签(占位符 "-"/"--" 不套底) | `title`、`value`、`tag`、`tagColor`("orange" 默认/"green") |
|
||||
| `PhoneFormRow` | 行式表单项(左标签+右值+分隔线),`PhoneFormRowType` = `INPUT`/`SELECT`(内置底部选择面板)/`DATE`/`TEXT`(只读右对齐,空值 "--") | `title`、`hint`、`type`、`value`(双向)、`list`、`required`、`enable`、`numeric`、`setRefreshCallBack` |
|
||||
|
||||
`PhoneBottomBar` 追加 `actionDanger` 属性:true 时主按钮转浅红底红字 + 垃圾桶图标(ULD 管理批量删除)。
|
||||
新增资源:`bg_phone_btn_danger`、`bg_phone_fab`(圆形浮动 + 按钮)、`bg_phone_tag_red_light`(浅红标签)、
|
||||
`ic_phone_trash`、`ic_phone_edit`、色值 `phone_danger_bg/text`。
|
||||
出港计重追加:`bg_phone_btn_weak`(浅蓝弱按钮,卡片内提前运抵)、`bg_phone_card_top_accent`
|
||||
(卡片顶部主色描边条)、`ic_phone_clipboard`(FAB 剪贴板)、`ic_phone_weight`(砝码图标,可 tint)。
|
||||
出港查询追加:`ic_phone_package`(件数)、`ic_phone_tag`(特码)、`bg_phone_dot_blue/gray`(状态圆点);
|
||||
`PhoneFilterPanel` 内容插槽已内置 ScrollView(筛选项超屏时收缩滚动,底部按钮始终可见——
|
||||
9 个筛选项曾把「重置/确认」挤出屏幕)。
|
||||
出港仓库追加:`bg_phone_btn_disabled`(灰底禁用按钮,与 `bg_phone_btn_weak` 同 10dp 圆角,
|
||||
卡片操作按钮禁用态配 `phone_tag_gray_text` 文字)。
|
||||
进港移库追加:`bg_phone_btn_green`(绿色主按钮,拍照上传「确认提交」)、`bg_phone_card_header_orange`
|
||||
(橙色详情卡表头,异常照片)、`bg_phone_photo_add`(虚线加号槽位)、色值 `phone_amber`。
|
||||
|
||||
⚠️ **弹层与浮动按钮的层级**:`PhoneFilterPanel` 默认无 elevation,页面里有带 elevation 的浮动按钮(FAB)时
|
||||
会穿透到弹层之上——给弹层实例加 `android:elevation="8dp"`(大于 FAB 的 6dp)。
|
||||
|
||||
**`PhoneFilterPanel` 自带内容插槽**:把 `PhoneDataLayout` 直接写在其标签内即可,组件会自动移入面板中部
|
||||
并施加 20dp 项间距,无需关心内部层级:
|
||||
|
||||
```xml
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
panelVisible="@{activity.filterPanelVisible}"
|
||||
setOnResetClick="@{(v)-> activity.resetFilter()}"
|
||||
setOnConfirmClick="@{(v)-> activity.confirmFilter()}"
|
||||
setOnDismissClick="@{(v)-> activity.toggleFilterPanel()}">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||
title='@{"航班日期"}' hint='@{"请选择航班日期"}'
|
||||
type="@{PhoneDataLayoutType.DATE}" value="@={viewModel.flightDate}" />
|
||||
|
||||
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
|
||||
```
|
||||
|
||||
#### ⚠️ 复合自定义 View 必须屏蔽子控件状态恢复(已踩坑)
|
||||
|
||||
同一页面放多个 `PhoneDataLayout`/`PhoneStatBox` 时,它们的内部子控件(`et`/`tv`/`spinner`…)**共用同一套 id**。
|
||||
系统按 id 保存/恢复视图状态会跨实例串档 —— 实测表现为:DATE 项的默认日期被邻近输入框恢复的空文本清空,
|
||||
并通过双向绑定把 ViewModel 字段也写成了空值(筛选条件静默丢失)。
|
||||
|
||||
因此每个含内部 id 的手机版组件都重写了:
|
||||
|
||||
```kotlin
|
||||
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) = dispatchFreezeSelfOnly(container)
|
||||
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) = dispatchThawSelfOnly(container)
|
||||
```
|
||||
|
||||
**新增同类组件时必须照做**(取值由 ViewModel + DataBinding 持有,本就不需要视图级恢复)。
|
||||
|
||||
### 已完成 / 待办
|
||||
|
||||
- ✅ **已完成**:适配框架 + 手机端公共资源 + 7 个手机版公共组件
|
||||
- ✅ **已完成页面**:
|
||||
- 「国际出港移库」`IntExpMoveActivity` + 「运单详情」`IntExpMoveDetailActivity`(框架参考页)
|
||||
- 「国际出港出库交接」`IntExpOutHandoverActivity` + 「板箱详情」`IntExpOutHandoverDetailActivity`
|
||||
(公共组件首个完整落地页,双端实机验证通过)
|
||||
- 「ULD管理」`UldListActivity` + `UldEditActivity`(app 模块首个适配页;一套编辑布局覆盖
|
||||
新增/修改/详情三态;批量删除为前端循环调用单条接口;状态三态/来源等 5 项后端缺口见问题清单 #7~#11)
|
||||
- 「出港计重」全链路 4+1 页:`GjcWeighingListActivity`(待计重列表,三格统计 + 卡片单票提前运抵 +
|
||||
FAB 进记录)、`GjcWeighingStartActivity`(开始计重,托盘数量/自重按设计稿手输)、
|
||||
`GjcWeighingRecordListActivity`(计重记录)、`GjcWeighingRecordDetailsActivity`(计重明细,
|
||||
isEditMode 复用为手机全屏「修改记录」覆层)+ 手机专属新页 `GjcWeighingWbDetailActivity`(运单详情)。
|
||||
搜索分流规则(实测):含字母→fno、11 位数字→wbNo、其余→likeNo(likeNo 按 8 位 no 匹配并非模糊);
|
||||
checkIn 过滤等 7 项后端缺口见问题清单 #12~#18
|
||||
- 「出港运抵」主列表 + 3 个手机专属新页:`IntExpArriveActivity`(待运抵/已运抵 Tab,客户端按
|
||||
`declareStatus` 是否为空拆分数据集,卡片长按浮层:待运抵→补充、已运抵→重置+回执)、
|
||||
`IntExpArriveSubOrderActivity`(分单明细,数据直接复用主列表已加载的 `haWbList`,零新增请求)、
|
||||
`IntExpArriveReceiptActivity`(回执内容只读页,复用 `haWbList[].response`)、
|
||||
`IntExpArriveSupplementActivity`(补充信息,参照 Pad 端国际进港「收发货人信息」批量应用模式,
|
||||
后端接口不存在,实机 404 已确认,见问题清单 #19)。
|
||||
Tab 过滤两个坑:① **RecyclerView 里 `View.GONE` 不会让 item 塌陷为 0 高度**(不同于普通
|
||||
ViewGroup),必须在数据层整体 `refresh()` 过滤后的子集给 adapter,不能给 item 根节点加
|
||||
visibility 绑定(实机截图验证过,见「常见编译错误速查」);② 通过 `Intent.putExtra(Serializable)`
|
||||
传递的 `GjcHaWb`/`GjcMaWb`,其 `checked`/`showMore`/`actionMenuVisible` 字段标了
|
||||
`@Transient`(= JVM `transient`,Java 序列化会跳过),反序列化后为 null,直接
|
||||
`.checked.set(...)` 会崩溃——接收方 `initOnCreated` 必须对列表 `.map { it.copy() }` 补上。
|
||||
2026-07-30 review 修正:状态重置弹框改手机底部弹层(弹框双变体首例,见改造步骤第 6 条)、
|
||||
主列表/回执页补空态、分单浮层补「回执」、分单统计加分母、Tab 角标初始 0
|
||||
- 「提取出库」`IntImpPickUpDLVActivity`(module_gjj,类型2 多选批量页 + 手机专属详情新页
|
||||
`IntImpPickUpDetailActivity`(Intent 传 Serializable bean 零请求)。待出库/已出库 Tab 绑
|
||||
**文档定义入参 `outState`**(0/1,抓包证实携带,效果待内网 #29)+ 角标双 pageQueryTotal;
|
||||
双形态卡片按 `isPickedUp`(dlvTime 非空)切换;手机进入时清空平板默认「当天缴费日期」查全量;
|
||||
缺口 #28:无 fdate 入参、出参无车牌(提货车辆)/目的港/业务类型。
|
||||
⚠️ 踩坑:`checkAllClick` 全选后底部「已选 N 项」不更新——批量勾选不走 FlowBus 单卡事件,
|
||||
必须在 checkAllClick 里同步 selectedCountText)
|
||||
- 「进港查询」`IntImpQueryActivity`(module_gjj;06 出港查询的进港镜像:三格统计
|
||||
总票数/已入库/已出库为已加载数据客户端计数(`pageQueryTotal` 无分项出参,缺口 #30),
|
||||
卡片状态前端推断:`dlvTime` 非空=已出库(绿)/`inDate` 非空=已入库(橙)/兜底未入库(灰);
|
||||
筛选弹层 9 项**入参全部经 api-doc 核对真实存在**(分页参数名 `page`/`limit`,品名参数名
|
||||
`goods`);详情页与运单追踪复用 08/06 已适配页零新增;平板侧滑筛选面板代码已判空守卫。
|
||||
⚠️ 踩坑:代理人绑定不能写 `bean.agentName ?? bean.agentCode`——转换器把 JSON null 归一成
|
||||
空串,`??` 只判 null 不判空串,须用 Bean 计算属性 `agentDisplay`(06 出港查询同步修正))
|
||||
- 「进港移库」`GnjMoveStashListActivity`(app 模块,**2026-08-03 归属更正后重做**:设计稿 11
|
||||
实际归属**国内进港移库 DomImpMove**(后端 8 接口齐全、Pad 首页「国内进港→进港移库」既有页面),
|
||||
最初误判为国际进港并按 IntExpMove 约定预接了不存在的 IntImpMove 模块(原缺口 #31),该套
|
||||
module_gjj 前端实现已整体删除。现行实现:主列表双形态(Pad 布局移 layout-sw600dp 零改动),
|
||||
待移库/已移库 Tab 走 **`search`/`searchMoved` 双端点**(`searchMoved` 出参无移库标识,前端按
|
||||
端点给 bean 打 `isMoved`),Tab 角标 = 两端点各调一次 limit=1 取分页 total(`initPhoneExtras`
|
||||
开关,仅手机发起);手机专属新页 `GnjMoveStashDetailActivity`(Intent 传 bean 零请求,三卡片:
|
||||
基本信息/移库信息(仅已移库)/异常照片)+ `GnjMoveStashPhotoActivity`(进入先调 `detail` 回填
|
||||
已有照片——`searchMoved` 出参无 pic 三字段 #34,提交走 **`modify` 最小体**
|
||||
`{mawbId,remark,picNumber,pic,originalPic}`,与平板移交编辑页同链路);批量移库复用平板
|
||||
`moveStashClick`(`move`:fid+ids)。手机首页入口在**国内 Tab**(`GnViewModel`,权限串
|
||||
`GnjYiKu=AppDomImpMove`,需补通用 route 跳转逻辑,图标从 module_gnj 复制);
|
||||
GnjMoveStashListActivity 补 @Route `/app/GnjMoveStashListActivity`。
|
||||
2026-08-03 内网真实数据双端验证通过;筛选 A/B:`wbNo`/`awbType` 生效,
|
||||
`fdate`/`fno`/`spCode` 被后端静默忽略(#32)、`opId`/`opDate` 恒 null(#33))
|
||||
- 「进港仓库」`IntImpStorageUseActivity`(module_gjj,07 出港仓库的进港镜像;**首页菜单实际跳转页,
|
||||
旧版 `GjjWareHouseActivity` 路由已注释弃用**。差异:卡片/筛选用始发站(fdep 入参)替代目的站;
|
||||
搜索框「运单号/航班号」分流(含字母→fno、其余→wbNo,抓包证实);筛选 5 项无入参(缺口 #25,
|
||||
比出港多 2 项);连带首次适配进港详情 `IntImpQueryDetailsActivity` + 3 Fragment(报文区 3 项:
|
||||
原始舱单/理货报告/海关放行,件重区直取 inPc/inWeight 无需求和,detail keys 以平板绑定为准 +
|
||||
dep/dest1/dest/unNumber/opDate 按出港推断待实测 #27);修改库位手机弹层「原库位」只列未出库
|
||||
库位(沿用平板校验);**前端接口前缀是 `IntImpStorage` 不是 `IntImpStorageUse`**,api-doc 按
|
||||
后者搜不到)
|
||||
- 「出港仓库」`IntExpStorageUseActivity`(嵌套多选列表页型的首个适配:平板「勾选主/子列表 +
|
||||
底部批量操作」在手机上改为**卡片级四按钮**(清仓/修改库位/出库/入库,已清仓置灰),语义等价
|
||||
「勾选该主单+全部库位」直接复用既有 performXxx 链路,ViewHolder 对双侧独有 id 全判空;
|
||||
三格统计 总票数/未清仓/已清仓(后两格 = 同条件 + `clearNormal=0/1` 各调一次 pageQueryTotal,
|
||||
过滤是否生效待内网 A/B,缺口 #24);筛选弹层 8 项中运单类型/业务类型/品名(中) 为后端未定义
|
||||
入参(缺口 #23,预留传参);三个操作弹框双变体化 + 手机版修改库位弹层加「原库位」单选;
|
||||
出库确认 AlertDialog → ConfirmDialogModel;详情与运单追踪完全复用出港查询已适配页,零新增页面)
|
||||
- 「出港查询」`GjcQueryActivity`(列表:三格统计 总票数/已入库/已离港 + 卡片状态标签,
|
||||
状态为前端推断:`fclose` 非空=已离港/`opDate` 非空=已入库;筛选弹层 9 项,日期起止与代理
|
||||
从平板搜索行并入,特码改下拉 `getSpecialCodeList(flag=1, ieFlag="")`;分项统计为已加载
|
||||
数据客户端计数,`isFclose` 实测被后端忽略,缺口 #21)+ `GjcQueryDetailsActivity`(3 Tab
|
||||
复用 ViewPager2+Fragment,手机变体 PhoneKvItem 网格;入库件数/重量= warehouseList 求和;
|
||||
锁定状态/计费重量后端无字段,缺口 #22)+ 「运单追踪」`LogDetailActivity`(app 模块公共页
|
||||
双变体首例:手机竖排时间线由 Activity `buildVerticalSteps` 动态构建,平板横向步骤条不动;
|
||||
手机标题按形态切「运单追踪」)。平板侧滑筛选面板代码已判空守卫(手机变体无 filter_panel
|
||||
include,Binding 字段为 @Nullable)
|
||||
- ✅ **设计稿 11 个入口(01~11)已于 2026-07-31 全部适配完成**
|
||||
- ⏳ **待办**:
|
||||
- `PhoneSearchBar` 接入 AutoQuery 联想(`AutoQueryManager` 目前只对接了 Pad 系组件)
|
||||
- 进港移库筛选三项(航班日期/航班号/特码)待后端实装 `fdate`/`fno`/`spCode` 入参(缺口 #32)、
|
||||
移库人/移库时间待后端回填 `opId`/`opDate` 并补姓名出参(缺口 #33)后自动生效
|
||||
- 出库交接 Tab 角标恒 0:`IntExpOutHandover/pageQueryTotal` 出参恒 0(后端缺陷,问题清单 #6),
|
||||
后端修复后角标自动恢复(`handoverState` 过滤本身已于 2026-07-29 实机验证通过)生效
|
||||
- 📋 **接口对接**:适配中发现的后端接口缺口统一记录在 `documents/后端接口对接问题清单.xlsx`
|
||||
(按模块/页面/接口定义/接口地址索引,与后端对接的正式文档,每页适配完必须维护);
|
||||
接口入参/出参一律以 api-doc MCP 为准核对,流程见
|
||||
`.claude/skills/phone-adapt/references/api-doc.md`——**后端对不认识的参数静默忽略**,
|
||||
猜参数名会得出「后端不支持」的误判(出库交接 `hoState`→`handoverState` 已踩过)
|
||||
|
||||
### 调试便利:直接拉起页面
|
||||
|
||||
`app/src/debug/AndroidManifest.xml`(**仅 Debug 生效,不进 Release 包**)通过 `tools:replace` 将适配调试页临时置为 `exported="true"`,免去每次登录再逐级点入:
|
||||
|
||||
```bash
|
||||
adb shell am start -n com.lukouguoji.aerologic/com.lukouguoji.gjc.page.move.IntExpMoveActivity
|
||||
adb shell am start -n com.lukouguoji.aerologic/com.lukouguoji.gjc.activity.IntExpOutHandoverActivity
|
||||
```
|
||||
|
||||
新增调试页时在该文件追加对应 `<activity>` 即可。
|
||||
|
||||
> 模拟器通常连不上内网服务(提示「接口访问失败」),列表为空。若需验证卡片/详情的还原度,
|
||||
> 可在 Activity 里临时注入几条样例 Bean(`binding.rv.commonAdapter()?.refresh(list)`)截图比对,**验证后务必删除**。
|
||||
|
||||
**参考文件**:`module_base/.../util/DeviceUtil.kt`、`module_gjc/res/layout/activity_int_exp_move.xml`(手机)、`module_gjc/res/layout-sw600dp/activity_int_exp_move.xml`(平板)
|
||||
|
||||
---
|
||||
|
||||
## Import 路径速查
|
||||
|
||||
### 基类与常用类
|
||||
@@ -870,6 +1251,7 @@ adb logcat | grep "com.lukouguoji.aerologic" # 日志
|
||||
| 类 | 正确路径 |
|
||||
|----|----------|
|
||||
| `DictUtils` | `com.lukouguoji.module_base.util.DictUtils` |
|
||||
| `DeviceUtil` | `com.lukouguoji.module_base.util.DeviceUtil` |
|
||||
| `MediaUtil` | `com.lukouguoji.module_base.util.MediaUtil` |
|
||||
| `UploadUtil` | `com.lukouguoji.module_base.util.UploadUtil` |
|
||||
| `KeyValue` | `dev.utils.app.info.KeyValue` |
|
||||
@@ -896,6 +1278,17 @@ adb logcat | grep "com.lukouguoji.aerologic" # 日志
|
||||
| 资源引用不存在 | drawable/color/string 缺失 | 先检查资源是否存在,不存在则创建或用已有资源 |
|
||||
| `View.VISIBLE` 报错 | 未导入 | XML `<data>` 中加 `<import type="android.view.View" />` |
|
||||
| `textStyle` DataBinding 报错 | 不支持 | 用固定值 `android:textStyle="bold"` |
|
||||
| 手机版页面一进入就 `ClassCastException` | 用了「不同文件名 + 代码判断」切换布局,生成了两个 Binding 类 | 改用同名布局 + 资源限定符(`layout/` 与 `layout-sw600dp/`),见「5.5 寸手持端双形态适配规范」 |
|
||||
| 布局变体编译报 `variable not found` | 两个变体的 `<data>` 中 `<variable>` 不一致 | 手机与平板变体的变量名/类型必须完全相同 |
|
||||
| 手机上元素小一半 / 布局错位 | AutoSize 仍按平板 720dp 基准换算 | 确认 `MyApplication.initAutoSizeConfig()` 中手机竖屏走 390×844 分支 |
|
||||
| 手机版筛选项默认值(如日期)自动被清空 | 多个复合自定义 View 内部子控件 id 相同,系统按 id 恢复状态时跨实例串档 | 组件重写 `dispatchSaveInstanceState`/`dispatchRestoreInstanceState` 为 `dispatchFreezeSelfOnly`/`dispatchThawSelfOnly`(见「手机版公共组件库」) |
|
||||
| 手机上页面先横屏再转竖屏(闪一下) | Manifest 写死 `screenOrientation="userLandscape"`,系统建窗口时已按横屏,代码纠正为时已晚 | 改成 `android:screenOrientation="unspecified"`,由 `BaseActivity` 按形态锁定(见「屏幕方向」小节) |
|
||||
| 平板先竖屏再转横屏 + 交互后 UI 放大约 1.6 倍 | Manifest 用了 `@integer/screen_orientation` 资源引用;系统解析 Manifest 不套用限定符,全部取到竖屏值,AutoSize 随之选了平板竖屏 720dp 基准 | Manifest 改回 `unspecified`(见「屏幕方向」小节的 ⛔ 说明) |
|
||||
| Tab/分类切换后列表顶部留一大块空白,卡片数量却不对 | 给 item 根节点加了 `visibility="@{tab条件 ? VISIBLE : GONE}"`——RecyclerView/LinearLayoutManager 不会像普通 ViewGroup 那样让 GONE 的子项自动塌陷为 0 高度,仍按测量高度参与滚动区域计算 | 不要用 item 级 visibility 做过滤;在 ViewModel 里自行维护完整列表,Tab 切换时把过滤后的子集整体 `commonAdapter()?.refresh(filteredList)` 给 adapter |
|
||||
| 通过 `Intent.putExtra(Serializable)` 传递的 Bean,切换其 `checked`/`showMore` 等状态时崩溃 `NullPointerException: ... on a null object reference` | 这些字段标了 `@Transient`(= JVM `transient`,本意是避免 Gson 网络请求序列化 `ObservableBoolean`),但 Java 对象序列化(Intent 传递走的机制)会跳过 `transient` 字段的恢复,反序列化后是 null | 接收方 `initOnCreated` 里对列表做 `.map { it.copy() }`——`.copy()` 会重新走一遍类体初始化逻辑,用全新 `ObservableBoolean(false)` 补上 |
|
||||
| 布局构建报 `Error: The string "--" is not permitted within comments` | XML 注释中出现连续双横线(如写"兜底 \"--\""),XML 规范禁止 | 注释里改用文字描述(如「兜底双横线」),代码绑定表达式中的 `"--"` 不受影响 |
|
||||
| 多选页手机版点「全选」后底部「已选 N 项」不更新(单卡勾选正常) | 批量勾选在 `checkAllClick` 里直接 set,不走 FlowBus 的单卡 EVENT_CHECK_CHANGED 事件,`selectedCountText` 只在事件回调里更新 | `checkAllClick` 末尾自行同步 `selectedCountText`(平板布局不绑定该字段,无影响) |
|
||||
| XML 里 `bean.a ?? bean.b` 兜底不生效,字段明明有值却显示占位符 | 网络层转换器把 JSON null 归一成空串 `""`,DataBinding 的 `??` 只判 null 不判空串,永远取到左侧的空串 | 在 Bean 上加计算属性做 `isNullOrEmpty` 双兜底(如 `agentDisplay`),XML 绑计算属性(进港查询/出港查询代理人已踩过) |
|
||||
|
||||
---
|
||||
|
||||
@@ -961,10 +1354,16 @@ private fun loadDictLists() {
|
||||
```xml
|
||||
<activity android:name="com.lukouguoji.gjc.activity.XxxActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="false" android:screenOrientation="userLandscape" />
|
||||
android:exported="false"
|
||||
android:screenOrientation="unspecified" />
|
||||
```
|
||||
> `screenOrientation` **必须写 `unspecified`**,不要写死 `userLandscape`、也不要用资源引用。
|
||||
> 写死横屏会让手机端「先横屏、约 1 秒后转竖屏」闪一下;资源引用则会让平板端出问题
|
||||
> (详见下方「屏幕方向」小节)。
|
||||
|
||||
3. 在 `ARouterConstants` 注册路由(如需)
|
||||
4. 标题栏统一用 `<include layout="@layout/title_tool_bar" />`,Activity 中 `setBackArrow("标题")`
|
||||
5. **双形态布局**:需同时支持手机的页面,平板布局放 `res/layout-sw600dp/`、手机布局放 `res/layout/`,两者**同名**且变量与共用 id 一致。详见「5.5 寸手持端双形态适配规范」
|
||||
|
||||
### 常见业务操作
|
||||
|
||||
@@ -1112,6 +1511,9 @@ Glide.with(itemView.context).load(glideUrl).into(binding.ivThumbnail)
|
||||
- `ic_new_expand`(全部展开/收起)36dp + padding 4dp
|
||||
- **图标之间间距统一 `marginStart=16dp`**(禁止使用 8dp,会导致视觉过近)
|
||||
- 常用资源: `bg_white_radius_8`、`colorPrimary`、`text_normal`、`text_gray`、`color_bottom_layout`
|
||||
- **双形态适配:逻辑与 UI 严格分离** — 手机版只重写布局,业务逻辑一律复用现有 ViewModel / ViewHolder;纯 UI 交互(Tab 切换、弹层显隐)写在 Activity,不下沉到 ViewModel
|
||||
- **禁止用不同文件名切换布局** — 手机/平板布局必须同名 + 资源限定符,否则 `ClassCastException`(详见「5.5 寸手持端双形态适配规范」)
|
||||
- 手机版资源统一使用 `phone_*` 颜色与 `bg_phone_*` drawable,禁止直接写死色值
|
||||
|
||||
### 环境配置
|
||||
|
||||
@@ -1125,4 +1527,16 @@ Glide.with(itemView.context).load(glideUrl).into(binding.ivThumbnail)
|
||||
2. 资源引用错误 → 检查 drawable/color/string 是否存在
|
||||
3. DataBinding 错误 → 检查 import、枚举值、View 类导入
|
||||
4. suspend function 错误 → 在 `viewModelScope.launch` 中调用
|
||||
5. 仍有问题 → `./gradlew clean` 后重新构建
|
||||
5. 双形态布局问题 → 查「常见编译错误速查」中 3 条布局变体相关条目;可用 `DeviceUtil.describe()` 打日志确认当前形态
|
||||
6. 仍有问题 → `./gradlew clean` 后重新构建
|
||||
|
||||
### 双端验证命令
|
||||
|
||||
```bash
|
||||
~/Library/Android/sdk/emulator/emulator -list-avds # 可用:Medium_Phone_API_36.1(手机)、Aerologic_Tablet(平板)
|
||||
adb -s <设备> shell wm size && adb -s <设备> shell wm density # 确认屏幕参数
|
||||
adb -s <设备> logcat -d -s BaseActivity:D # 查看形态识别结果 PHONE(sw=411dp) / TABLET(sw=800dp)
|
||||
adb -s <设备> exec-out screencap -p > /tmp/shot.png # 截图比对
|
||||
```
|
||||
|
||||
> 改动 `BaseActivity` / `BaseBindingActivity` / 公共组件后,**必须在平板上回归**,确认现有平板版行为不变。
|
||||
|
||||
627
README.md
627
README.md
@@ -1,301 +1,326 @@
|
||||
# AirLogistics - 航空物流信息管理系统
|
||||
|
||||
一款专业的航空物流管理Android应用,为机场货运站提供全流程数字化解决方案,涵盖国内外货物进出港、仓储管理、车辆调度等核心业务。
|
||||
|
||||
## 📋 项目信息
|
||||
|
||||
- **版本**: 1.7.9 (versionCode 79)
|
||||
- **包名**: com.lukouguoji.aerologic
|
||||
- **最低支持**: Android 7.0 (API 24)
|
||||
- **目标版本**: Android 10 (API 30)
|
||||
- **开发语言**: Kotlin + Java
|
||||
- **架构模式**: MVVM + 组件化
|
||||
|
||||
## 🏗️ 架构设计
|
||||
|
||||
### 核心架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ 应用层 (App) │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 国内出港 │ 国内进港 │ 国际出港 │ 国际进港 │
|
||||
│ (GNC) │ (GNJ) │ (GJC) │ (GJJ) │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 基础服务层 (module_base) │
|
||||
│ • MVVM基类 • 网络框架 • UI组件 • 工具类 │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 蓝牙打印 │ 图表库 │ 航班管理 │ 货物追踪 │
|
||||
│ (Printer) │ (Chart) │ (Hangban) │ (Cargo) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### MVVM模式
|
||||
|
||||
- **View层**: Activity/Fragment + DataBinding
|
||||
- **ViewModel层**: BaseViewModel / BasePageViewModel
|
||||
- **Model层**: Repository + Retrofit API
|
||||
- **数据流**: 协程(Coroutines) + Flow
|
||||
|
||||
### 组件化架构
|
||||
|
||||
- **模块独立**: 通过`isBuildModule`参数控制模块独立运行或集成
|
||||
- **路由通信**: ARouter实现模块间页面跳转和通信
|
||||
- **事件总线**: FlowBus(基于Flow) + EventBus
|
||||
- **依赖注入**: 基于ServiceLoader的服务发现机制
|
||||
|
||||
### 基础设施
|
||||
|
||||
#### 核心基类
|
||||
- `BaseActivity`: 协程支持、Loading管理、扫码功能、键盘控制
|
||||
- `BaseBindingActivity`: DataBinding自动绑定、ViewModel生命周期管理
|
||||
- `BaseViewModel`: Loading管理、Activity结果处理
|
||||
- `BasePageViewModel`: 分页列表专用,集成PageModel自动处理分页
|
||||
- `CommonAdapter + BaseViewHolder`: 统一列表适配器封装
|
||||
|
||||
#### 网络请求层
|
||||
- **ServiceCreator**: Retrofit实例创建,动态IP配置
|
||||
- **拦截器**: 自动添加Token、时间戳,统一错误处理
|
||||
- **扩展函数**:
|
||||
- `launchCollect`: 无Loading的后台请求
|
||||
- `launchLoadingCollect`: 带Loading的关键操作
|
||||
- `toRequestBody`: Map/Bean自动转JSON
|
||||
|
||||
#### 统一UI组件
|
||||
- **PadSearchLayout**: 搜索输入框组合控件(支持文本/数字/下拉/日期)
|
||||
- **PadDataLayout**: 数据展示/编辑组合控件(标题对齐)
|
||||
- **StatusView**: 状态栏占位View
|
||||
- **统一样式**: 颜色、字体、圆角、间距的全局规范
|
||||
|
||||
## 🚀 技术栈
|
||||
|
||||
### 核心框架
|
||||
- **Kotlin**: 1.6.21 + Coroutines 1.6.0
|
||||
- **Jetpack**: ViewModel、LiveData、DataBinding
|
||||
- **网络**: Retrofit 2.6.1 + OkHttp 3.12.12
|
||||
- **JSON**: FastJSON 1.2.73 + Gson 2.10.1
|
||||
|
||||
### UI相关
|
||||
- **Material Design**: Material Components
|
||||
- **屏幕适配**: AutoSize 1.2.+ (横屏1152dp × 720dp)
|
||||
- **下拉刷新**: SmartRefreshLayout 2.0.3
|
||||
- **图表**: MPAndroidChart (定制版)
|
||||
- **弹窗**: XPopup 2.9.19
|
||||
- **图片**: Glide 4.15.1 + PictureSelector v3.11.2
|
||||
|
||||
### 功能组件
|
||||
- **路由**: ARouter 1.5.2
|
||||
- **扫码**: ZXing 2.2.9
|
||||
- **权限**: AndPermission 2.0.2
|
||||
- **打印**: 佳博SDK 2.0.4
|
||||
- **日志**: Timber 5.0.1
|
||||
|
||||
## 💼 核心业务模块
|
||||
|
||||
### 国内出港 (module_gnc)
|
||||
货物收运、复磅称重、转运管理、出库装机、仓库管理、数据统计
|
||||
|
||||
### 国内进港 (module_gnj)
|
||||
舱单管理、卸机入库、出库提货、移库管理、货物交接、实时查询
|
||||
|
||||
### 国际出港 (module_gjc)
|
||||
国际货物收运、板箱组装、ULD容器管理、复磅称重、仓储移库
|
||||
|
||||
### 国际进港 (module_gjj)
|
||||
国际舱单管理、报文解析、理货管理、出库操作、货物交接
|
||||
|
||||
### 支持模块
|
||||
- **航班管理** (module_hangban): 航班查询、航班统计
|
||||
- **货物追踪** (module_cargo): 货物状态追踪、运输日志
|
||||
- **监装监卸** (module_mit): 监装监卸管理
|
||||
- **PDA功能** (module_p): PDA专用功能
|
||||
- **蓝牙打印** (Printer): 佳博打印机SDK集成
|
||||
|
||||
## 📦 项目结构
|
||||
|
||||
```
|
||||
aerologic-app/
|
||||
├── app/ # 应用壳层,整合所有业务模块
|
||||
├── module_base/ # 核心基础库,提供所有通用能力
|
||||
│ ├── base/ # MVVM基类
|
||||
│ ├── http/ # 网络请求框架
|
||||
│ ├── ui/weight/ # 统一UI组件
|
||||
│ ├── ktx/ # Kotlin扩展函数
|
||||
│ ├── bean/ # 数据模型
|
||||
│ └── router/ # ARouter路由常量
|
||||
├── module_gnc/ # 国内出港业务
|
||||
├── module_gnj/ # 国内进港业务
|
||||
├── module_gjc/ # 国际出港业务
|
||||
├── module_gjj/ # 国际进港业务
|
||||
├── module_hangban/ # 航班查询管理
|
||||
├── module_cargo/ # 货物追踪查询
|
||||
├── module_mit/ # 监装监卸管理
|
||||
├── module_p/ # PDA专用功能
|
||||
├── Printer/ # 蓝牙打印模块
|
||||
├── MPChartLib/ # 定制图表库
|
||||
├── CLAUDE.md # 详细开发指南
|
||||
└── README.md # 项目说明文档
|
||||
```
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
- **IDE**: Android Studio Arctic Fox (2020.3.1) 或更高版本
|
||||
- **JDK**: 1.8
|
||||
- **Gradle**: 7.3.3
|
||||
- **Kotlin**: 1.6.21
|
||||
|
||||
### 依赖配置
|
||||
|
||||
如遇依赖下载问题:
|
||||
|
||||
1. 下载 gradle-7.3.3-bin.zip
|
||||
- 百度网盘: https://pan.baidu.com/s/18wsuGRlNxjMYbxLhBH9yeg
|
||||
- 提取码: 1029
|
||||
|
||||
2. 配置Gradle:
|
||||
- Settings → Build, Execution, Deployment → Build Tools → Gradle
|
||||
- 解压并替换到 "Gradle user home" 目录
|
||||
|
||||
### 构建项目
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone [repository-url]
|
||||
cd aerologic-app
|
||||
|
||||
# 清理构建
|
||||
./gradlew clean
|
||||
|
||||
# 构建Debug版本
|
||||
./gradlew assembleDebug
|
||||
|
||||
# 构建Release版本(已签名)
|
||||
./gradlew assembleRelease
|
||||
|
||||
# 安装到设备
|
||||
./gradlew installDebug
|
||||
```
|
||||
|
||||
### 组件化开发模式
|
||||
|
||||
支持模块独立运行调试:
|
||||
|
||||
1. 编辑 `gradle.properties`
|
||||
2. 设置 `isBuildModule=true` (独立模块) 或 `false` (集成模式)
|
||||
3. Sync项目并运行对应模块
|
||||
|
||||
## ⚙️ 配置说明
|
||||
|
||||
### 网络配置
|
||||
|
||||
服务器地址配置位置:
|
||||
- **文件**: `module_base/src/main/res/values/strings.xml`
|
||||
- **主服务器**: `system_url_inner`
|
||||
- **地磅服务器**: `weight_url`
|
||||
- **运行时**: 可通过SharedPreferences动态修改IP地址
|
||||
|
||||
### 签名配置
|
||||
|
||||
Release版本签名信息:
|
||||
- **KeyStore**: `key.jks` (项目根目录)
|
||||
- **密码**: storePassword/keyPassword均为 `123321`
|
||||
- **别名**: `key`
|
||||
|
||||
### 网络安全
|
||||
|
||||
- **超时时间**: 30秒(连接/读取/写入)
|
||||
- **认证方式**: Bearer Token(自动添加)
|
||||
- **HTTP支持**: 配置在 `res/xml/network_security_config.xml`
|
||||
|
||||
## 📚 开发文档
|
||||
|
||||
详细的开发指南请参考 **[CLAUDE.md](./CLAUDE.md)**,包含:
|
||||
|
||||
- ✅ 完整的架构详解(基类、网络、UI组件)
|
||||
- ✅ 标准开发模板(Activity、ViewModel、ViewHolder)
|
||||
- ✅ 实际业务示例(列表页、详情页、编辑页)
|
||||
- ✅ 开发检查清单(7步列表页、4步详情页、5步编辑页)
|
||||
- ✅ DataBinding适配器(10+种)
|
||||
- ✅ Kotlin扩展函数(20+种)
|
||||
- ✅ 常见业务场景实现
|
||||
|
||||
## 🔄 Git工作流
|
||||
|
||||
### 分支策略
|
||||
|
||||
- **develop**: 主开发分支
|
||||
- **feature/xxx**: 功能开发分支
|
||||
- **release/x.x.x**: 发布分支
|
||||
- **hotfix/xxx**: 紧急修复分支
|
||||
|
||||
### 当前分支
|
||||
|
||||
- **开发分支**: feature/hefei
|
||||
- **主分支**: develop(用于PR)
|
||||
|
||||
### 提交规范
|
||||
|
||||
提交前确保:
|
||||
- ✅ 代码编译通过
|
||||
- ✅ 无明显错误和警告
|
||||
- ✅ 遵循项目代码规范
|
||||
|
||||
## 📋 开发原则
|
||||
|
||||
1. ✅ **优先使用现有基类和封装**,避免重复造轮子
|
||||
2. ✅ **充分利用PadDataLayout和PadSearchLayout组件**,保证UI一致性
|
||||
3. ✅ **遵循统一的命名和目录组织规范**
|
||||
4. ✅ **使用DataBinding简化代码**,减少findViewById
|
||||
5. ✅ **利用扩展函数处理通用逻辑**(Toast、Dialog、权限等)
|
||||
6. ✅ **保持架构一致性**,新增业务参考现有模块
|
||||
|
||||
## 🔐 特性亮点
|
||||
|
||||
### 1. 高度组件化
|
||||
- 业务模块完全解耦,支持独立开发、测试、发布
|
||||
- 通过ARouter实现模块间通信,无需直接依赖
|
||||
|
||||
### 2. 统一UI规范
|
||||
- 自定义PadSearchLayout、PadDataLayout组合控件
|
||||
- 全局统一的颜色、字体、圆角、间距规范
|
||||
- 丰富的DataBinding适配器,简化UI开发
|
||||
|
||||
### 3. 高效网络框架
|
||||
- 协程+Flow的现代化异步方案
|
||||
- 统一的请求封装(launchCollect/launchLoadingCollect)
|
||||
- 自动的Loading管理和错误处理
|
||||
|
||||
### 4. 完善的基础设施
|
||||
- BaseActivity提供扫码、Loading、键盘控制等通用能力
|
||||
- BasePageViewModel自动处理分页逻辑
|
||||
- CommonAdapter统一列表适配器管理
|
||||
|
||||
### 5. 丰富的工具扩展
|
||||
- 20+ Kotlin扩展函数(Toast、Dialog、JSON、日期等)
|
||||
- 10+ DataBinding适配器(图片加载、可见性、Shape等)
|
||||
- 完整的业务开发模板和检查清单
|
||||
|
||||
## 🛠️ 技术特点
|
||||
|
||||
- **响应式编程**: Kotlin Coroutines + Flow
|
||||
- **声明式UI**: DataBinding双向绑定
|
||||
- **函数式编程**: Kotlin扩展函数和高阶函数
|
||||
- **依赖注入**: 基于ServiceLoader的轻量级方案
|
||||
- **模块化路由**: ARouter组件化通信
|
||||
- **横屏适配**: AutoSize自动适配1152dp × 720dp
|
||||
- **蓝牙打印**: 佳博打印机SDK深度集成
|
||||
- **二维码扫描**: ZXing高性能扫码
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目为企业内部系统,版权归所属公司所有。
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
**重要**: 本项目包含敏感业务信息和配置,仅供授权人员使用,严禁外传。
|
||||
|
||||
---
|
||||
|
||||
**文档维护**: 本README提供项目概览和架构信息,详细开发指南请参考 [CLAUDE.md](./CLAUDE.md)
|
||||
# AirLogistics - 航空物流信息管理系统
|
||||
|
||||
一款专业的航空物流管理Android应用,为机场货运站提供全流程数字化解决方案,涵盖国内外货物进出港、仓储管理、车辆调度等核心业务。
|
||||
|
||||
## 📋 项目信息
|
||||
|
||||
- **版本**: 1.7.9 (versionCode 79)
|
||||
- **包名**: com.lukouguoji.aerologic
|
||||
- **最低支持**: Android 7.0 (API 24)
|
||||
- **目标版本**: Android 10 (API 30)
|
||||
- **开发语言**: Kotlin + Java
|
||||
- **架构模式**: MVVM + 组件化
|
||||
|
||||
## 🏗️ 架构设计
|
||||
|
||||
### 核心架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ 应用层 (App) │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 国内出港 │ 国内进港 │ 国际出港 │ 国际进港 │
|
||||
│ (GNC) │ (GNJ) │ (GJC) │ (GJJ) │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 基础服务层 (module_base) │
|
||||
│ • MVVM基类 • 网络框架 • UI组件 • 工具类 │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 蓝牙打印 │ 图表库 │ 航班管理 │ 货物追踪 │
|
||||
│ (Printer) │ (Chart) │ (Hangban) │ (Cargo) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### MVVM模式
|
||||
|
||||
- **View层**: Activity/Fragment + DataBinding
|
||||
- **ViewModel层**: BaseViewModel / BasePageViewModel
|
||||
- **Model层**: Repository + Retrofit API
|
||||
- **数据流**: 协程(Coroutines) + Flow
|
||||
|
||||
### 组件化架构
|
||||
|
||||
- **模块独立**: 通过`isBuildModule`参数控制模块独立运行或集成
|
||||
- **路由通信**: ARouter实现模块间页面跳转和通信
|
||||
- **事件总线**: FlowBus(基于Flow) + EventBus
|
||||
- **依赖注入**: 基于ServiceLoader的服务发现机制
|
||||
|
||||
### 基础设施
|
||||
|
||||
#### 核心基类
|
||||
- `BaseActivity`: 协程支持、Loading管理、扫码功能、键盘控制
|
||||
- `BaseBindingActivity`: DataBinding自动绑定、ViewModel生命周期管理
|
||||
- `BaseViewModel`: Loading管理、Activity结果处理
|
||||
- `BasePageViewModel`: 分页列表专用,集成PageModel自动处理分页
|
||||
- `CommonAdapter + BaseViewHolder`: 统一列表适配器封装
|
||||
|
||||
#### 网络请求层
|
||||
- **ServiceCreator**: Retrofit实例创建,动态IP配置
|
||||
- **拦截器**: 自动添加Token、时间戳,统一错误处理
|
||||
- **扩展函数**:
|
||||
- `launchCollect`: 无Loading的后台请求
|
||||
- `launchLoadingCollect`: 带Loading的关键操作
|
||||
- `toRequestBody`: Map/Bean自动转JSON
|
||||
|
||||
#### 统一UI组件
|
||||
- **PadSearchLayout**: 搜索输入框组合控件(支持文本/数字/下拉/日期)
|
||||
- **PadDataLayout**: 数据展示/编辑组合控件(标题对齐)
|
||||
- **StatusView**: 状态栏占位View
|
||||
- **统一样式**: 颜色、字体、圆角、间距的全局规范
|
||||
|
||||
#### 双形态适配(平板 / 手机)
|
||||
同时支持 **10 寸平板横屏** 与 **5.5 寸手机竖屏**,业务逻辑层完全复用,只重写布局。
|
||||
|
||||
- **DeviceUtil**: 设备形态判定(按 `smallestScreenWidthDp >= 600` 区分),提供调试用强制形态开关
|
||||
- **布局自动切换**: 通过 Android 资源限定符实现,`res/layout-sw600dp/`(平板)与 `res/layout/`(手机)下放**同名**布局,DataBinding 生成同一个 Binding 类,Activity / ViewModel / ViewHolder 无需任何设备判断
|
||||
- **屏幕方向**: Manifest 统一写 `android:screenOrientation="unspecified"`,系统按设备当前物理方向建窗口,`BaseActivity.applyDeviceOrientation()` 再按形态锁定(手机竖屏 / 平板横屏),两端均无「先横屏再转竖屏」闪屏。**不要改用资源引用**——系统解析 Manifest 不套用限定符,会导致平板取到竖屏值并触发 AutoSize 放大
|
||||
- **AutoSize 基准**: 横屏 1152×720 / 手机竖屏 390×844 / 平板竖屏 720×1280
|
||||
- **手机端资源**: `phone_*` 配色 + `bg_phone_*` drawable + 手机版公共标题栏
|
||||
|
||||
> ⚠️ 禁止用「不同文件名 + 代码判断」切换布局,会生成两个 Binding 类导致运行时 `ClassCastException`。
|
||||
> 详见 [`documents/5.5寸手持端适配技术方案.md`](documents/5.5寸手持端适配技术方案.md) 与 `CLAUDE.md` 对应章节。
|
||||
|
||||
## 🚀 技术栈
|
||||
|
||||
### 核心框架
|
||||
- **Kotlin**: 1.6.21 + Coroutines 1.6.0
|
||||
- **Jetpack**: ViewModel、LiveData、DataBinding
|
||||
- **网络**: Retrofit 2.6.1 + OkHttp 3.12.12
|
||||
- **JSON**: FastJSON 1.2.73 + Gson 2.10.1
|
||||
|
||||
### UI相关
|
||||
- **Material Design**: Material Components
|
||||
- **屏幕适配**: AutoSize 1.2.+ (横屏1152dp × 720dp)
|
||||
- **下拉刷新**: SmartRefreshLayout 2.0.3
|
||||
- **图表**: MPAndroidChart (定制版)
|
||||
- **弹窗**: XPopup 2.9.19
|
||||
- **图片**: Glide 4.15.1 + PictureSelector v3.11.2
|
||||
|
||||
### 功能组件
|
||||
- **路由**: ARouter 1.5.2
|
||||
- **扫码**: ZXing 2.2.9
|
||||
- **权限**: AndPermission 2.0.2
|
||||
- **打印**: 佳博SDK 2.0.4
|
||||
- **日志**: Timber 5.0.1
|
||||
|
||||
## 💼 核心业务模块
|
||||
|
||||
### 国内出港 (module_gnc)
|
||||
货物收运、复磅称重、转运管理、出库装机、仓库管理、数据统计
|
||||
|
||||
### 国内进港 (module_gnj)
|
||||
舱单管理、卸机入库、出库提货、移库管理、货物交接、实时查询
|
||||
|
||||
### 国际出港 (module_gjc)
|
||||
国际货物收运、板箱组装、ULD容器管理、复磅称重、仓储移库
|
||||
|
||||
### 国际进港 (module_gjj)
|
||||
国际舱单管理、报文解析、理货管理、出库操作、货物交接
|
||||
|
||||
### 支持模块
|
||||
- **航班管理** (module_hangban): 航班查询、航班统计
|
||||
- **货物追踪** (module_cargo): 货物状态追踪、运输日志
|
||||
- **监装监卸** (module_mit): 监装监卸管理
|
||||
- **PDA功能** (module_p): PDA专用功能
|
||||
- **蓝牙打印** (Printer): 佳博打印机SDK集成
|
||||
|
||||
## 📦 项目结构
|
||||
|
||||
```
|
||||
aerologic-app/
|
||||
├── app/ # 应用壳层,整合所有业务模块
|
||||
├── module_base/ # 核心基础库,提供所有通用能力
|
||||
│ ├── base/ # MVVM基类
|
||||
│ ├── http/ # 网络请求框架
|
||||
│ ├── ui/weight/ # 统一UI组件
|
||||
│ ├── ktx/ # Kotlin扩展函数
|
||||
│ ├── bean/ # 数据模型
|
||||
│ ├── util/ # 工具类(含 DeviceUtil 设备形态判定)
|
||||
│ └── router/ # ARouter路由常量
|
||||
├── module_gnc/ # 国内出港业务
|
||||
├── module_gnj/ # 国内进港业务
|
||||
├── module_gjc/ # 国际出港业务
|
||||
├── module_gjj/ # 国际进港业务
|
||||
├── module_hangban/ # 航班查询管理
|
||||
├── module_cargo/ # 货物追踪查询
|
||||
├── module_mit/ # 监装监卸管理
|
||||
├── module_p/ # PDA专用功能
|
||||
├── Printer/ # 蓝牙打印模块
|
||||
├── MPChartLib/ # 定制图表库
|
||||
├── documents/ # 设计稿、报价单、技术方案文档
|
||||
├── CLAUDE.md # 详细开发指南
|
||||
└── README.md # 项目说明文档
|
||||
```
|
||||
|
||||
各业务模块内的布局按设备形态分目录存放:
|
||||
|
||||
```
|
||||
module_gjc/src/main/res/
|
||||
├── layout/ # 手机布局(5.5 寸竖屏,兜底)
|
||||
└── layout-sw600dp/ # 平板布局(10 寸横屏)
|
||||
```
|
||||
|
||||
> 两目录下**同名**的布局是同一布局的资源变体,系统按屏幕最小宽度自动选择。
|
||||
> 尚未适配手机的页面,布局保留在 `layout/` 即可(两端共用),支持渐进式适配。
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
- **IDE**: Android Studio Arctic Fox (2020.3.1) 或更高版本
|
||||
- **JDK**: 1.8
|
||||
- **Gradle**: 7.3.3
|
||||
- **Kotlin**: 1.6.21
|
||||
|
||||
### 依赖配置
|
||||
|
||||
如遇依赖下载问题:
|
||||
|
||||
1. 下载 gradle-7.3.3-bin.zip
|
||||
- 百度网盘: https://pan.baidu.com/s/18wsuGRlNxjMYbxLhBH9yeg
|
||||
- 提取码: 1029
|
||||
|
||||
2. 配置Gradle:
|
||||
- Settings → Build, Execution, Deployment → Build Tools → Gradle
|
||||
- 解压并替换到 "Gradle user home" 目录
|
||||
|
||||
### 构建项目
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone [repository-url]
|
||||
cd aerologic-app
|
||||
|
||||
# 清理构建
|
||||
./gradlew clean
|
||||
|
||||
# 构建Debug版本
|
||||
./gradlew assembleDebug
|
||||
|
||||
# 构建Release版本(已签名)
|
||||
./gradlew assembleRelease
|
||||
|
||||
# 安装到设备
|
||||
./gradlew installDebug
|
||||
```
|
||||
|
||||
### 组件化开发模式
|
||||
|
||||
支持模块独立运行调试:
|
||||
|
||||
1. 编辑 `gradle.properties`
|
||||
2. 设置 `isBuildModule=true` (独立模块) 或 `false` (集成模式)
|
||||
3. Sync项目并运行对应模块
|
||||
|
||||
## ⚙️ 配置说明
|
||||
|
||||
### 网络配置
|
||||
|
||||
服务器地址配置位置:
|
||||
- **文件**: `module_base/src/main/res/values/strings.xml`
|
||||
- **主服务器**: `system_url_inner`
|
||||
- **地磅服务器**: `weight_url`
|
||||
- **运行时**: 可通过SharedPreferences动态修改IP地址
|
||||
|
||||
### 签名配置
|
||||
|
||||
Release版本签名信息:
|
||||
- **KeyStore**: `key.jks` (项目根目录)
|
||||
- **密码**: storePassword/keyPassword均为 `123321`
|
||||
- **别名**: `key`
|
||||
|
||||
### 网络安全
|
||||
|
||||
- **超时时间**: 30秒(连接/读取/写入)
|
||||
- **认证方式**: Bearer Token(自动添加)
|
||||
- **HTTP支持**: 配置在 `res/xml/network_security_config.xml`
|
||||
|
||||
## 📚 开发文档
|
||||
|
||||
详细的开发指南请参考 **[CLAUDE.md](./CLAUDE.md)**,包含:
|
||||
|
||||
- ✅ 完整的架构详解(基类、网络、UI组件)
|
||||
- ✅ 标准开发模板(Activity、ViewModel、ViewHolder)
|
||||
- ✅ 实际业务示例(列表页、详情页、编辑页)
|
||||
- ✅ 开发检查清单(7步列表页、4步详情页、5步编辑页)
|
||||
- ✅ DataBinding适配器(10+种)
|
||||
- ✅ Kotlin扩展函数(20+种)
|
||||
- ✅ 常见业务场景实现
|
||||
|
||||
## 🔄 Git工作流
|
||||
|
||||
### 分支策略
|
||||
|
||||
- **develop**: 主开发分支
|
||||
- **feature/xxx**: 功能开发分支
|
||||
- **release/x.x.x**: 发布分支
|
||||
- **hotfix/xxx**: 紧急修复分支
|
||||
|
||||
### 当前分支
|
||||
|
||||
- **开发分支**: feature/hefei
|
||||
- **主分支**: develop(用于PR)
|
||||
|
||||
### 提交规范
|
||||
|
||||
提交前确保:
|
||||
- ✅ 代码编译通过
|
||||
- ✅ 无明显错误和警告
|
||||
- ✅ 遵循项目代码规范
|
||||
|
||||
## 📋 开发原则
|
||||
|
||||
1. ✅ **优先使用现有基类和封装**,避免重复造轮子
|
||||
2. ✅ **充分利用PadDataLayout和PadSearchLayout组件**,保证UI一致性
|
||||
3. ✅ **遵循统一的命名和目录组织规范**
|
||||
4. ✅ **使用DataBinding简化代码**,减少findViewById
|
||||
5. ✅ **利用扩展函数处理通用逻辑**(Toast、Dialog、权限等)
|
||||
6. ✅ **保持架构一致性**,新增业务参考现有模块
|
||||
|
||||
## 🔐 特性亮点
|
||||
|
||||
### 1. 高度组件化
|
||||
- 业务模块完全解耦,支持独立开发、测试、发布
|
||||
- 通过ARouter实现模块间通信,无需直接依赖
|
||||
|
||||
### 2. 统一UI规范
|
||||
- 自定义PadSearchLayout、PadDataLayout组合控件
|
||||
- 全局统一的颜色、字体、圆角、间距规范
|
||||
- 丰富的DataBinding适配器,简化UI开发
|
||||
|
||||
### 3. 高效网络框架
|
||||
- 协程+Flow的现代化异步方案
|
||||
- 统一的请求封装(launchCollect/launchLoadingCollect)
|
||||
- 自动的Loading管理和错误处理
|
||||
|
||||
### 4. 完善的基础设施
|
||||
- BaseActivity提供扫码、Loading、键盘控制等通用能力
|
||||
- BasePageViewModel自动处理分页逻辑
|
||||
- CommonAdapter统一列表适配器管理
|
||||
|
||||
### 5. 丰富的工具扩展
|
||||
- 20+ Kotlin扩展函数(Toast、Dialog、JSON、日期等)
|
||||
- 10+ DataBinding适配器(图片加载、可见性、Shape等)
|
||||
- 完整的业务开发模板和检查清单
|
||||
|
||||
## 🛠️ 技术特点
|
||||
|
||||
- **响应式编程**: Kotlin Coroutines + Flow
|
||||
- **声明式UI**: DataBinding双向绑定
|
||||
- **函数式编程**: Kotlin扩展函数和高阶函数
|
||||
- **依赖注入**: 基于ServiceLoader的轻量级方案
|
||||
- **模块化路由**: ARouter组件化通信
|
||||
- **横屏适配**: AutoSize自动适配1152dp × 720dp
|
||||
- **蓝牙打印**: 佳博打印机SDK深度集成
|
||||
- **二维码扫描**: ZXing高性能扫码
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目为企业内部系统,版权归所属公司所有。
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
**重要**: 本项目包含敏感业务信息和配置,仅供授权人员使用,严禁外传。
|
||||
|
||||
---
|
||||
|
||||
**文档维护**: 本README提供项目概览和架构信息,详细开发指南请参考 [CLAUDE.md](./CLAUDE.md)
|
||||
|
||||
180
app/src/debug/AndroidManifest.xml
Normal file
180
app/src/debug/AndroidManifest.xml
Normal file
@@ -0,0 +1,180 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
仅 Debug 构建生效的 Manifest 覆盖(不会合并进 Release 包)
|
||||
|
||||
用途:手机版布局适配调试期间,允许通过 adb 直接拉起指定页面,
|
||||
免去每次都要登录并从首页菜单逐级点入。
|
||||
|
||||
adb shell am start -n com.lukouguoji.aerologic/com.lukouguoji.gjc.page.move.IntExpMoveActivity
|
||||
|
||||
注意:这里只放调试便利配置,正式包的 exported 仍为 app/src/main/AndroidManifest.xml 中的 false。
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application>
|
||||
|
||||
<!-- 出港移库(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.page.move.IntExpMoveActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港移库-运单详情(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.page.move.IntExpMoveDetailActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出库交接(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.IntExpOutHandoverActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出库交接-板箱详情(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.IntExpOutHandoverDetailActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- ULD管理(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.aerologic.page.uld.list.UldListActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- ULD新增/修改/详情(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.aerologic.page.uld.edit.UldEditActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港待计重(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.GjcWeighingListActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港计重-开始计重(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.GjcWeighingStartActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港计重-计重记录(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.GjcWeighingRecordListActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港计重-计重明细(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.GjcWeighingRecordDetailsActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港计重-运单详情(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.GjcWeighingWbDetailActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港运抵(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.IntExpArriveActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港运抵-分单明细(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.IntExpArriveSubOrderActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港运抵-回执内容(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.IntExpArriveReceiptActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港运抵-补充信息(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.IntExpArriveSupplementActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港查询(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.GjcQueryActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港查询-详情(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.GjcQueryDetailsActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 运单追踪/操作日志详情(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.aerologic.page.log.detail.LogDetailActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 出港仓库(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjc.activity.IntExpStorageUseActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 进港仓库(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjj.activity.IntImpStorageUseActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 进港查询-详情(手机版适配验证页,进港仓库卡片点击进入) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjj.activity.IntImpQueryDetailsActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 提取出库(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjj.activity.IntImpPickUpDLVActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 提取出库-运单详情(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjj.activity.IntImpPickUpDetailActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 进港查询(手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.gjj.activity.IntImpQueryActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 进港移库(国内进港,手机版适配验证页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 进港移库-运单详情(手机版专属页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
<!-- 进港移库-拍照上传(手机版专属页) -->
|
||||
<activity
|
||||
android:name="com.lukouguoji.aerologic.page.gnj.move.stash.photo.GnjMoveStashPhotoActivity"
|
||||
android:exported="true"
|
||||
tools:replace="android:exported" />
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
package com.lukouguoji.aerologic.page.gnj.move.stash.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.google.gson.Gson
|
||||
import com.lukouguoji.aerologic.R
|
||||
import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashDetailBinding
|
||||
import com.lukouguoji.aerologic.page.gnj.move.stash.photo.GnjMoveStashPhotoActivity
|
||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||
import com.lukouguoji.module_base.bean.MoveStashBean
|
||||
import com.lukouguoji.module_base.common.Constant
|
||||
|
||||
/**
|
||||
* 国内进港移库「运单详情」(5.5 寸手持端专属页面,无平板变体)
|
||||
*
|
||||
* 由列表卡片的「查看详情」进入;「异常照片」卡片的加号槽位跳拍照上传页。
|
||||
* 展示字段全部由列表项带入(Gson JSON),零新增请求。
|
||||
*/
|
||||
class GnjMoveStashDetailActivity :
|
||||
BaseBindingActivity<ActivityGnjMoveStashDetailBinding, GnjMoveStashDetailViewModel>() {
|
||||
|
||||
override fun layoutId() = R.layout.activity_gnj_move_stash_detail
|
||||
override fun viewModelClass() = GnjMoveStashDetailViewModel::class.java
|
||||
|
||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||
setBackArrow("运单详情")
|
||||
binding.viewModel = viewModel
|
||||
binding.activity = this
|
||||
viewModel.initOnCreated(intent)
|
||||
}
|
||||
|
||||
/** 「异常照片」加号槽位 → 拍照上传页 */
|
||||
fun openPhoto() {
|
||||
viewModel.dataBean.value?.let {
|
||||
GnjMoveStashPhotoActivity.start(this, it.mawbId, it.wbNo)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun start(context: Context, bean: MoveStashBean) {
|
||||
context.startActivity(
|
||||
Intent(context, GnjMoveStashDetailActivity::class.java)
|
||||
.putExtra(Constant.Key.DATA, Gson().toJson(bean))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.lukouguoji.aerologic.page.gnj.move.stash.detail
|
||||
|
||||
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.MoveStashBean
|
||||
import com.lukouguoji.module_base.common.Constant
|
||||
|
||||
/**
|
||||
* 国内进港移库「运单详情」ViewModel(5.5 寸手持端专属页面)
|
||||
*
|
||||
* 列表项数据已包含详情页所需全部字段(含 isMoved 端点打标),直接由列表带入,不额外请求。
|
||||
*/
|
||||
class GnjMoveStashDetailViewModel : BaseViewModel() {
|
||||
|
||||
val dataBean = MutableLiveData(MoveStashBean())
|
||||
|
||||
fun initOnCreated(intent: Intent) {
|
||||
val json = intent.getStringExtra(Constant.Key.DATA).orEmpty()
|
||||
if (json.isNotEmpty()) {
|
||||
dataBean.value = Gson().fromJson(json, MoveStashBean::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,104 @@
|
||||
package com.lukouguoji.aerologic.page.gnj.move.stash.list
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.lukouguoji.aerologic.R
|
||||
import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashListBinding
|
||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||
import com.lukouguoji.module_base.ktx.getLifecycleOwner
|
||||
|
||||
class GnjMoveStashListActivity :
|
||||
BaseBindingActivity<ActivityGnjMoveStashListBinding, GnjMoveStashListViewModel>() {
|
||||
override fun layoutId() = R.layout.activity_gnj_move_stash_list
|
||||
|
||||
override fun viewModelClass() = GnjMoveStashListViewModel::class.java
|
||||
|
||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||
setBackArrow("国内进港移库")
|
||||
|
||||
binding.viewModel = viewModel
|
||||
|
||||
viewModel.pageModel
|
||||
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner())
|
||||
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun start(context: Context) {
|
||||
val starter = Intent(context, GnjMoveStashListActivity::class.java)
|
||||
context.startActivity(starter)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package com.lukouguoji.aerologic.page.gnj.move.stash.list
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.databinding.ObservableBoolean
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.lukouguoji.aerologic.R
|
||||
import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashListBinding
|
||||
import com.lukouguoji.aerologic.page.gnj.move.stash.photo.GnjMoveStashPhotoActivity
|
||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||
import com.lukouguoji.module_base.bean.MoveStashBean
|
||||
import com.lukouguoji.module_base.common.ConstantEvent
|
||||
import com.lukouguoji.module_base.impl.FlowBus
|
||||
import com.lukouguoji.module_base.impl.observe
|
||||
import com.lukouguoji.module_base.ktx.getLifecycleOwner
|
||||
import com.lukouguoji.module_base.router.ARouterConstants
|
||||
import com.lukouguoji.module_base.util.DeviceUtil
|
||||
|
||||
@Route(path = ARouterConstants.ACTIVITY_URL_GNJ_MOVE_STASH_LIST)
|
||||
class GnjMoveStashListActivity :
|
||||
BaseBindingActivity<ActivityGnjMoveStashListBinding, GnjMoveStashListViewModel>() {
|
||||
override fun layoutId() = R.layout.activity_gnj_move_stash_list
|
||||
|
||||
override fun viewModelClass() = GnjMoveStashListViewModel::class.java
|
||||
|
||||
/** 筛选弹层显隐(仅手机 UI 状态,平板布局不绑定) */
|
||||
val filterPanelVisible = ObservableBoolean(false)
|
||||
|
||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||
// 设计稿手机端标题为「进港移库」,平板保持原标题
|
||||
setBackArrow(if (DeviceUtil.isPhone()) "进港移库" else "国内进港移库")
|
||||
|
||||
binding.viewModel = viewModel
|
||||
binding.activity = this
|
||||
|
||||
viewModel.pageModel
|
||||
.bindSmartRefreshLayout(binding.srl, binding.rv, viewModel, getLifecycleOwner())
|
||||
|
||||
if (DeviceUtil.isPhone()) {
|
||||
// 手机专属:Tab 角标 + 特码字典等额外请求
|
||||
viewModel.initPhoneExtras()
|
||||
|
||||
// 卡片勾选变化 → 更新全选状态与已选计数
|
||||
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) {
|
||||
viewModel.updateCheckAllStatus()
|
||||
}
|
||||
}
|
||||
|
||||
// 拍照上传等子页面完成后刷新列表(平板无发射方,观察无副作用)
|
||||
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
// ==================== 筛选弹层(仅手机) ====================
|
||||
|
||||
fun toggleFilterPanel() {
|
||||
filterPanelVisible.set(!filterPanelVisible.get())
|
||||
}
|
||||
|
||||
/** 筛选:重置(只清空弹层内条件,不影响 Tab 与顶部搜索框) */
|
||||
fun resetFilter() {
|
||||
viewModel.flightDate.value = ""
|
||||
viewModel.flightNo.value = ""
|
||||
viewModel.spCode.value = ""
|
||||
viewModel.businessType.value = ""
|
||||
}
|
||||
|
||||
fun confirmFilter() {
|
||||
filterPanelVisible.set(false)
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
// ==================== 卡片长按「拍照」浮层(仅手机) ====================
|
||||
|
||||
/** 卡片长按:展开操作浮层(View.OnLongClickListener 要求返回 Boolean) */
|
||||
fun showCardActionPhone(bean: MoveStashBean): Boolean {
|
||||
bean.actionMenuVisible.set(true)
|
||||
return true
|
||||
}
|
||||
|
||||
/** 点浮层空白处关闭 */
|
||||
fun hideCardActionPhone(bean: MoveStashBean) {
|
||||
bean.actionMenuVisible.set(false)
|
||||
}
|
||||
|
||||
/** 浮层「拍照」→ 拍照上传页 */
|
||||
fun openPhoto(bean: MoveStashBean) {
|
||||
bean.actionMenuVisible.set(false)
|
||||
GnjMoveStashPhotoActivity.start(this, bean.mawbId, bean.wbNo)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun start(context: Context) {
|
||||
val starter = Intent(context, GnjMoveStashListActivity::class.java)
|
||||
context.startActivity(starter)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,34 +3,64 @@ package com.lukouguoji.aerologic.page.gnj.move.stash.list
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import com.lukouguoji.aerologic.databinding.ItemGnjMoveStashListBinding
|
||||
import com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailActivity
|
||||
import com.lukouguoji.gnj.page.yiku.handover.GnjYiKuHandoverActivity
|
||||
import com.lukouguoji.module_base.base.BaseViewHolder
|
||||
import com.lukouguoji.module_base.bean.MoveStashBean
|
||||
import com.lukouguoji.module_base.common.ConstantEvent
|
||||
import com.lukouguoji.module_base.impl.FlowBus
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* 国内进港移库列表项 ViewHolder(双形态共用)
|
||||
*
|
||||
* 平板/手机布局差异较大,单侧独有的控件 id 在 Binding 基类中为 @Nullable,
|
||||
* 全部走判空调用(平板:swipe_menu/btn_edit/ll;手机:ll_detail)。
|
||||
*/
|
||||
class GnjMoveStashListViewHolder(view: View) :
|
||||
BaseViewHolder<MoveStashBean, ItemGnjMoveStashListBinding>(view) {
|
||||
|
||||
override fun onBind(item: Any?, position: Int) {
|
||||
val bean = getItemBean(item)!!
|
||||
binding.bean = bean
|
||||
binding.position = position
|
||||
binding.activity = itemView.context as? GnjMoveStashListActivity
|
||||
|
||||
// 勾选框(双形态共有):切换选中;手机端另行广播刷新全选/已选计数
|
||||
binding.ivIcon.setOnClickListener {
|
||||
bean.checked.set(!bean.checked.get())
|
||||
binding.executePendingBindings()
|
||||
|
||||
GlobalScope.launch {
|
||||
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).emit("check_changed")
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 平板独有交互 =====
|
||||
|
||||
// 点击列表项进入移交详情
|
||||
binding.ll.setOnClickListener {
|
||||
binding.ll?.setOnClickListener {
|
||||
GnjYiKuHandoverActivity.startForDetails(itemView.context, bean.mawbId)
|
||||
}
|
||||
|
||||
// 侧滑菜单 - 编辑按钮
|
||||
binding.btnEdit.setOnClickListener {
|
||||
binding.swipeMenu.quickClose()
|
||||
binding.btnEdit?.setOnClickListener {
|
||||
binding.swipeMenu?.quickClose()
|
||||
if (bean.pickFlag == "1") {
|
||||
Toast.makeText(itemView.context, "已出库,不可编辑", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
GnjYiKuHandoverActivity.startForEdit(itemView.context, bean.mawbId)
|
||||
}
|
||||
|
||||
// ===== 手机独有交互 =====
|
||||
|
||||
// 「查看详情」→ 手机版运单详情页
|
||||
binding.llDetail?.setOnClickListener {
|
||||
GnjMoveStashDetailActivity.start(itemView.context, bean)
|
||||
}
|
||||
|
||||
binding.executePendingBindings()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,41 @@ class GnjMoveStashListViewModel : BasePageViewModel() {
|
||||
val number = MutableLiveData("0")
|
||||
val weight = MutableLiveData("0")
|
||||
|
||||
// ========== 5.5 寸手持端专属字段(平板布局不绑定,默认值保证平板行为零变化) ==========
|
||||
|
||||
/** 移库状态 Tab:"0" 待移库(DomImpMove/search)/ "1" 已移库(DomImpMove/searchMoved) */
|
||||
val moveState = MutableLiveData("0")
|
||||
|
||||
/** 手机筛选弹层:航班日期 / 航班号 / 特码(默认空 → 不进请求) */
|
||||
val flightDate = MutableLiveData("")
|
||||
val flightNo = MutableLiveData("")
|
||||
val spCode = MutableLiveData("")
|
||||
val spCodeList = MutableLiveData<List<KeyValue>>(emptyList())
|
||||
|
||||
val moveStateTabs = MutableLiveData(
|
||||
listOf(KeyValue("待移库", "0"), KeyValue("已移库", "1"))
|
||||
)
|
||||
|
||||
/** 状态 Tab 角标(与 [moveStateTabs] 等长,取两端点分页 total) */
|
||||
val moveStateCounts = MutableLiveData(listOf("", ""))
|
||||
|
||||
/** 底部操作条「已选 N 项」 */
|
||||
val selectedCount = MutableLiveData("0")
|
||||
|
||||
/** 全选状态(手机 PhoneBottomBar 勾选图标) */
|
||||
val isAllChecked = MutableLiveData(false)
|
||||
|
||||
/** 手机专属的额外请求(Tab 角标、特码字典)开关,仅手机形态在 Activity 中开启 */
|
||||
private var phoneExtrasEnabled = false
|
||||
|
||||
fun initPhoneExtras() {
|
||||
phoneExtrasEnabled = true
|
||||
// 特码字典:flag=0 国内、ieFlag=I 进港(是否生效待实测,见问题清单)
|
||||
DictUtils.getSpecialCodeList(addAll = false, flag = 0, ieFlag = "I") { list ->
|
||||
spCodeList.postValue(listOf(KeyValue("全部", "")) + list)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 方法区
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@@ -95,25 +130,73 @@ class GnjMoveStashListViewModel : BasePageViewModel() {
|
||||
}
|
||||
|
||||
|
||||
/** 查询条件(分页参数除外),search / searchMoved / searchTotal 三处共用同一套入参 */
|
||||
private fun buildFilterParams(): Map<String, Any?> = mapOf(
|
||||
"awbType" to businessType.value!!.ifEmpty { null },
|
||||
"wbNo" to waybill.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() {
|
||||
val body = mapOf(
|
||||
val body = (buildFilterParams() + mapOf(
|
||||
"page" to pageModel.page,
|
||||
"limit" to pageModel.limit,
|
||||
"awbType" to businessType.value!!.ifEmpty { null },
|
||||
"wbNo" to waybill.value!!.ifEmpty { null },
|
||||
"carrier" to carrier.value!!.ifEmpty { null },
|
||||
).toRequestBody()
|
||||
)).toRequestBody()
|
||||
val moved = moveState.value == "1"
|
||||
launchLoadingCollect({
|
||||
NetApply.api.getGnjMoveStashList(
|
||||
body
|
||||
)
|
||||
// 平板 moveState 恒为 "0" → 恒走 search(原行为);已移库 Tab 走 searchMoved
|
||||
if (moved) NetApply.api.getGnjMoveStashMovedList(body)
|
||||
else NetApply.api.getGnjMoveStashList(body)
|
||||
}) {
|
||||
onSuccess = {
|
||||
pageModel.handleListBean(it)
|
||||
// searchMoved 出参无移库标识,按端点打标(卡片形态/详情移库信息卡依赖)
|
||||
it.list?.forEach { bean -> bean.isMoved = moved }
|
||||
pageModel.handleListBean(it) { updateCheckAllStatus() }
|
||||
count.value = it.total
|
||||
}
|
||||
}
|
||||
getCountData(body)
|
||||
if (phoneExtrasEnabled) loadTabCounts()
|
||||
}
|
||||
|
||||
/** Tab 角标:search / searchMoved 各以 limit=1 查一次取分页 total(仅手机形态发起) */
|
||||
private fun loadTabCounts() {
|
||||
val body = (buildFilterParams() + mapOf("page" to 1, "limit" to 1)).toRequestBody()
|
||||
launchCollect({ NetApply.api.getGnjMoveStashList(body) }) {
|
||||
onSuccess = { updateTabCount(0, it.total) }
|
||||
}
|
||||
launchCollect({ NetApply.api.getGnjMoveStashMovedList(body) }) {
|
||||
onSuccess = { updateTabCount(1, it.total) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTabCount(index: Int, total: Int?) {
|
||||
val current = moveStateCounts.value?.toMutableList() ?: mutableListOf("", "")
|
||||
if (index < current.size) {
|
||||
current[index] = (total ?: 0).toString()
|
||||
moveStateCounts.value = current
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 手机端全选 / 已选计数 ==========
|
||||
|
||||
fun toggleSelectAll() {
|
||||
val list = pageModel.rv?.commonAdapter()?.items
|
||||
?.filterIsInstance<MoveStashBean>() ?: return
|
||||
CheckUtil.handleAllCheck(list)
|
||||
updateCheckAllStatus()
|
||||
pageModel.rv?.commonAdapter()?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun updateCheckAllStatus() {
|
||||
val list = pageModel.rv?.commonAdapter()?.items
|
||||
?.filterIsInstance<MoveStashBean>() ?: return
|
||||
isAllChecked.value = list.isNotEmpty() && list.all { it.checked.get() }
|
||||
selectedCount.value = list.count { it.checked.get() }.toString()
|
||||
}
|
||||
|
||||
private fun getCountData(body: RequestBody) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.lukouguoji.aerologic.page.gnj.move.stash.photo
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.lukouguoji.aerologic.R
|
||||
import com.lukouguoji.aerologic.databinding.ActivityGnjMoveStashPhotoBinding
|
||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||
import com.lukouguoji.module_base.common.Constant
|
||||
import com.lukouguoji.module_base.ktx.addOnItemClickListener
|
||||
|
||||
/**
|
||||
* 国内进港移库「拍照上传」(5.5 寸手持端专属页面,无平板变体)
|
||||
*
|
||||
* 由列表卡片长按浮层「拍照」或运单详情「异常照片」加号槽位进入。
|
||||
* 已有照片经 DomImpMove/detail 回填,提交走 DomImpMove/modify
|
||||
* (mawbId + pic/originalPic/picNumber 三字段规范,与平板移交编辑页同一链路)。
|
||||
*/
|
||||
class GnjMoveStashPhotoActivity :
|
||||
BaseBindingActivity<ActivityGnjMoveStashPhotoBinding, GnjMoveStashPhotoViewModel>() {
|
||||
|
||||
override fun layoutId() = R.layout.activity_gnj_move_stash_photo
|
||||
override fun viewModelClass() = GnjMoveStashPhotoViewModel::class.java
|
||||
|
||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||
setBackArrow("拍照上传")
|
||||
binding.viewModel = viewModel
|
||||
|
||||
viewModel.rv = binding.rv
|
||||
binding.rv.addOnItemClickListener(viewModel)
|
||||
|
||||
viewModel.initOnCreated(intent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun start(context: Context, mawbId: String, wbNo: String) {
|
||||
context.startActivity(
|
||||
Intent(context, GnjMoveStashPhotoActivity::class.java)
|
||||
.putExtra(Constant.Key.ID, mawbId)
|
||||
.putExtra(Constant.Key.CODE, wbNo)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.lukouguoji.aerologic.page.gnj.move.stash.photo
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lukouguoji.module_base.base.BaseViewModel
|
||||
import com.lukouguoji.module_base.bean.FileBean
|
||||
import com.lukouguoji.module_base.bean.GnjYiKuBean
|
||||
import com.lukouguoji.module_base.common.Constant
|
||||
import com.lukouguoji.module_base.common.ConstantEvent
|
||||
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.launchCollect
|
||||
import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
||||
import com.lukouguoji.module_base.ktx.noNull
|
||||
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.Dispatchers
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* 国内进港移库「拍照上传」ViewModel(5.5 寸手持端专属页面)
|
||||
*
|
||||
* 图片选择/预览/删除复用 module_base 的 ImageSelectNewViewHolder(末位加号槽位)。
|
||||
* 已有照片经 DomImpMove/detail 回填;提交走 DomImpMove/modify 最小体
|
||||
* (mawbId/remark/picNumber/pic/originalPic),与平板移交编辑页同一链路。
|
||||
*/
|
||||
class GnjMoveStashPhotoViewModel : BaseViewModel(), IOnItemClickListener {
|
||||
|
||||
var mawbId = ""
|
||||
|
||||
/** 运单号(副标题展示,由列表带入) */
|
||||
val wbNo = MutableLiveData("")
|
||||
|
||||
/** detail 回填的完整 bean(提交时保留 remark 等原值) */
|
||||
private var detailBean = GnjYiKuBean()
|
||||
|
||||
// 图片网格适配器配置(复用公共图片选择组件)
|
||||
val itemViewHolder = ImageSelectNewViewHolder::class.java
|
||||
val itemLayoutId = com.lukouguoji.module_base.R.layout.item_image_select_new
|
||||
|
||||
var rv: RecyclerView? = null
|
||||
|
||||
fun initOnCreated(intent: Intent) {
|
||||
mawbId = intent.getStringExtra(Constant.Key.ID).orEmpty()
|
||||
wbNo.value = intent.getStringExtra(Constant.Key.CODE).orEmpty()
|
||||
|
||||
// 已有照片回填(pic 缩略图 + originalPic 原图),末位补加号槽位
|
||||
launchCollect({ NetApply.api.getGnjMoveStashDetail(mawbId) }) {
|
||||
onSuccess = {
|
||||
detailBean = it.data ?: GnjYiKuBean()
|
||||
val picList = detailBean.pic.split(",").filter { p -> p.isNotEmpty() }
|
||||
val originalList = detailBean.originalPic.split(",").filter { p -> p.isNotEmpty() }
|
||||
val images = picList.mapIndexed { index, picUrl ->
|
||||
FileBean(
|
||||
path = MediaUtil.fillUrl(picUrl),
|
||||
url = picUrl,
|
||||
originalPic = MediaUtil.fillUrl(originalList.getOrElse(index) { picUrl })
|
||||
)
|
||||
}
|
||||
rv?.post {
|
||||
rv?.commonAdapter()?.refresh(images + FileBean())
|
||||
}
|
||||
}
|
||||
onFailed = { _, _ ->
|
||||
// 详情取不到也允许直接新增照片
|
||||
rv?.post { rv?.commonAdapter()?.refresh(listOf(FileBean())) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认提交:先补传未上传的本地图片,再按三字段规范提交 modify
|
||||
*/
|
||||
fun submitClick() {
|
||||
(rv?.commonAdapter()?.items ?: emptyList())
|
||||
.asFlow()
|
||||
.map { it as FileBean }
|
||||
.filter { it.path.isNotEmpty() && it.url.isEmpty() }
|
||||
.onEach {
|
||||
val data = UploadUtil.upload(it.path).data
|
||||
// UploadUtil 返回:newName=原图(较大),zipFileName=缩略图(较小)
|
||||
it.url = data?.zipFileName ?: ""
|
||||
it.originalPic = data?.newName ?: ""
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.onStart { showLoading() }
|
||||
.catch {
|
||||
showToast(it.message.noNull("上传图片失败"))
|
||||
dismissLoading()
|
||||
}
|
||||
.onCompletion {
|
||||
launchLoadingCollect({
|
||||
val list = (rv?.commonAdapter()?.items ?: emptyList())
|
||||
.filterIsInstance<FileBean>()
|
||||
.filter { it.path.isNotEmpty() }
|
||||
|
||||
// 最小体提交:只更新图片三字段(remark 保留 detail 原值),与平板移交编辑页一致
|
||||
val params = mapOf(
|
||||
"mawbId" to mawbId,
|
||||
"remark" to detailBean.remark,
|
||||
"picNumber" to list.size.toString(),
|
||||
"pic" to list.joinToString(",") { MediaUtil.removeUrl(it.url) },
|
||||
"originalPic" to list.joinToString(",") { MediaUtil.removeUrl(it.originalPic) },
|
||||
).toRequestBody(removeEmptyOrNull = true)
|
||||
|
||||
NetApply.api.modifyGnjMoveStash(params)
|
||||
}) {
|
||||
onSuccess = {
|
||||
showToast("提交成功")
|
||||
viewModelScope.launch {
|
||||
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
|
||||
}
|
||||
getTopActivity().finish()
|
||||
}
|
||||
onFailed = { _, msg ->
|
||||
showToast(msg.noNull("提交失败"))
|
||||
}
|
||||
}
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
/**
|
||||
* 「拍照/上传」按钮:直接唤起图片选择(含拍照入口)
|
||||
*/
|
||||
fun pickClick() {
|
||||
MediaUtil.pickImage(getTopActivity(), maxNum = 9) { results ->
|
||||
val adapter = rv?.commonAdapter() ?: return@pickImage
|
||||
results.forEach { result ->
|
||||
// 在加号槽位(最后一项)之前插入
|
||||
val insertPos = (adapter.items.size - 1).coerceAtLeast(0)
|
||||
adapter.items.add(insertPos, FileBean(path = result.realPath))
|
||||
adapter.notifyItemInserted(insertPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片网格点击回调(长按出删除角标 / 点删除角标移除)
|
||||
*/
|
||||
override fun onItemClick(position: Int, type: Int) {
|
||||
val adapter = rv?.commonAdapter() ?: return
|
||||
val bean = adapter.getItem(position) as? FileBean ?: return
|
||||
when (type) {
|
||||
com.lukouguoji.module_base.R.id.rl -> {
|
||||
if (bean.path.isNotEmpty()) {
|
||||
bean.canDelete.set(!bean.canDelete.get())
|
||||
}
|
||||
}
|
||||
|
||||
com.lukouguoji.module_base.R.id.iv_delete -> {
|
||||
adapter.removeItem(position)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||
import com.lukouguoji.module_base.bean.LogBean
|
||||
import com.lukouguoji.module_base.common.Constant
|
||||
import com.lukouguoji.module_base.router.ARouterConstants
|
||||
import com.lukouguoji.module_base.util.DeviceUtil
|
||||
|
||||
@Route(path = ARouterConstants.ACTIVITY_URL_LOG_DETAIL)
|
||||
class LogDetailActivity : BaseBindingActivity<ActivityLogDetailBinding, LogDetailViewModel>() {
|
||||
@@ -34,7 +35,8 @@ class LogDetailActivity : BaseBindingActivity<ActivityLogDetailBinding, LogDetai
|
||||
}
|
||||
|
||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||
setBackArrow("操作日志详情")
|
||||
// 手机版设计稿标题为「运单追踪」,平板保持原标题
|
||||
setBackArrow(if (DeviceUtil.isPhone()) "运单追踪" else "操作日志详情")
|
||||
binding.viewModel = viewModel
|
||||
|
||||
// 配置操作详情 RecyclerView(垂直时间线)
|
||||
@@ -58,7 +60,139 @@ class LogDetailActivity : BaseBindingActivity<ActivityLogDetailBinding, LogDetai
|
||||
val activeCodes = viewModel.activeStepCodes.value ?: emptySet()
|
||||
val latestCode = viewModel.latestStepCode.value ?: ""
|
||||
if (steps.isEmpty()) return
|
||||
buildStepProgressBar(steps, activeCodes, latestCode)
|
||||
// 手机布局是竖排时间线(layout/activity_log_detail.xml),平板保持横向步骤条
|
||||
if (DeviceUtil.isPhone()) {
|
||||
buildVerticalSteps(steps, activeCodes, latestCode)
|
||||
} else {
|
||||
buildStepProgressBar(steps, activeCodes, latestCode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机版竖排流转时间线(对应设计稿「运单追踪」):
|
||||
* 每个节点 = 左侧圆点 + 连接竖线,右侧步骤名 + 该环节最新操作时间。
|
||||
* 已发生节点绿色,未发生节点灰色。
|
||||
*/
|
||||
private fun buildVerticalSteps(
|
||||
steps: List<StepInfo>,
|
||||
activeCodes: Set<String>,
|
||||
latestCode: String
|
||||
) {
|
||||
val container = binding.llSteps
|
||||
container.removeAllViews()
|
||||
container.orientation = LinearLayout.VERTICAL
|
||||
|
||||
val logs = viewModel.statusLogList.value ?: emptyList()
|
||||
val colorGreen = 0xFF16A34A.toInt()
|
||||
val colorGray = 0xFF9CA3AF.toInt()
|
||||
val colorLine = 0xFFE5E7EB.toInt()
|
||||
|
||||
for (i in steps.indices) {
|
||||
val step = steps[i]
|
||||
val isActive = step.code in activeCodes
|
||||
val isLast = i == steps.size - 1
|
||||
val stepLog = logs.lastOrNull { it.status == step.code }
|
||||
val time = stepLog?.opDate ?: ""
|
||||
val statusContent = stepLog?.content ?: ""
|
||||
|
||||
val row = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
}
|
||||
|
||||
// 左列:圆点 + 连接竖线
|
||||
val dotColumn = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
layoutParams = LinearLayout.LayoutParams(dp(20), LinearLayout.LayoutParams.MATCH_PARENT)
|
||||
}
|
||||
val dot = View(this).apply {
|
||||
setBackgroundResource(
|
||||
if (isActive) com.lukouguoji.module_base.R.drawable.bg_phone_dot_green
|
||||
else com.lukouguoji.module_base.R.drawable.bg_phone_dot_gray
|
||||
)
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
dp(if (isActive) 12 else 8), dp(if (isActive) 12 else 8)
|
||||
).apply { topMargin = dp(if (isActive) 2 else 4) }
|
||||
}
|
||||
dotColumn.addView(dot)
|
||||
if (!isLast) {
|
||||
val line = View(this).apply {
|
||||
setBackgroundColor(colorLine)
|
||||
layoutParams = LinearLayout.LayoutParams(dp(2), 0).apply {
|
||||
weight = 1f
|
||||
topMargin = dp(2)
|
||||
bottomMargin = dp(2)
|
||||
}
|
||||
}
|
||||
dotColumn.addView(line)
|
||||
}
|
||||
row.addView(dotColumn)
|
||||
|
||||
// 右列:步骤名 + 时间
|
||||
val content = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply {
|
||||
weight = 1f
|
||||
leftMargin = dp(10)
|
||||
}
|
||||
setPadding(0, 0, 0, if (isLast) 0 else dp(24))
|
||||
}
|
||||
// 名称行:步骤名(左)+ 已发生节点的状态 pill(右,设计稿如「放行状态:01」)
|
||||
val nameRow = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
}
|
||||
nameRow.addView(TextView(this).apply {
|
||||
text = step.name
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 15f)
|
||||
setTextColor(if (isActive) colorGreen else colorGray)
|
||||
if (isActive) setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply { weight = 1f }
|
||||
})
|
||||
if (isActive && statusContent.isNotEmpty()) {
|
||||
nameRow.addView(TextView(this).apply {
|
||||
text = statusContent
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
|
||||
setTextColor(colorGreen)
|
||||
setBackgroundResource(com.lukouguoji.module_base.R.drawable.bg_phone_tag_green)
|
||||
setPadding(dp(8), dp(3), dp(8), dp(3))
|
||||
maxLines = 1
|
||||
ellipsize = android.text.TextUtils.TruncateAt.END
|
||||
maxWidth = dp(180)
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply { leftMargin = dp(8) }
|
||||
})
|
||||
}
|
||||
content.addView(nameRow)
|
||||
if (time.isNotEmpty()) {
|
||||
content.addView(TextView(this).apply {
|
||||
text = time
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
|
||||
setTextColor(colorGray)
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply { topMargin = dp(4) }
|
||||
})
|
||||
}
|
||||
row.addView(content)
|
||||
|
||||
container.addView(row)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildStepProgressBar(
|
||||
|
||||
@@ -7,7 +7,15 @@ import com.lukouguoji.aerologic.R
|
||||
import com.lukouguoji.aerologic.databinding.ActivityUldEditBinding
|
||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||
import com.lukouguoji.module_base.common.Constant
|
||||
import com.lukouguoji.module_base.common.DetailsPageType
|
||||
import com.lxj.xpopup.XPopup
|
||||
|
||||
/**
|
||||
* ULD 新增 / 修改 / 详情(一页三态,标题按 pageType 切换)。
|
||||
*
|
||||
* 布局 id 不区分设备,平板/手机由资源限定符自动选择:
|
||||
* layout-sw600dp/activity_uld_edit.xml(平板) / layout/activity_uld_edit.xml(手机)
|
||||
*/
|
||||
class UldEditActivity : BaseBindingActivity<ActivityUldEditBinding, UldEditViewModel>() {
|
||||
|
||||
override fun layoutId() = R.layout.activity_uld_edit
|
||||
@@ -18,6 +26,26 @@ class UldEditActivity : BaseBindingActivity<ActivityUldEditBinding, UldEditViewM
|
||||
viewModel.initOnCreate(intent)
|
||||
setBackArrow("ULD${viewModel.pageType.value!!.title}")
|
||||
binding.viewModel = viewModel
|
||||
binding.activity = this
|
||||
}
|
||||
|
||||
// ==================== 手机版专用交互(纯 UI) ====================
|
||||
|
||||
/** 详情态右上铅笔 → 关闭详情并进入修改页 */
|
||||
fun onEditClick() {
|
||||
start(this, viewModel.id, modify = true)
|
||||
finish()
|
||||
}
|
||||
|
||||
/** 状态行点击 → 底部选择面板(详情态只读不弹) */
|
||||
fun onStatusRowClick() {
|
||||
if (viewModel.pageType.value == DetailsPageType.Details) return
|
||||
val options = viewModel.statusPhoneList
|
||||
XPopup.Builder(this)
|
||||
.asBottomList("请选择状态", options.map { it.key }.toTypedArray()) { position, _ ->
|
||||
options.getOrNull(position)?.let { viewModel.onStatusSelected(it.value) }
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.lukouguoji.module_base.ktx.launchLoadingCollect
|
||||
import com.lukouguoji.module_base.ktx.noNull
|
||||
import com.lukouguoji.module_base.ktx.showToast
|
||||
import com.lukouguoji.module_base.ktx.toRequestBody
|
||||
import com.lukouguoji.module_base.util.DeviceUtil
|
||||
import com.lukouguoji.module_base.util.DictUtils
|
||||
import dev.DevUtils
|
||||
import dev.utils.app.info.KeyValue
|
||||
@@ -33,6 +34,25 @@ class UldEditViewModel : BaseViewModel() {
|
||||
// 所属航司下拉列表(数据源:POST /typeCode/intCarrier)
|
||||
val carrierList = MutableLiveData<List<KeyValue>>(emptyList())
|
||||
|
||||
// ========== 手机版(5.5 寸手持端)专用 ==========
|
||||
|
||||
/** 来源选项(本地枚举;后端字段暂不支持,需求已登记 documents/后端接口对接问题清单.xlsx) */
|
||||
val sourceEditList = MutableLiveData<List<KeyValue>>(
|
||||
listOf(
|
||||
KeyValue("空运", "空运"),
|
||||
KeyValue("陆运", "陆运"),
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* 手机版状态选项。设计稿为三态(可用/占用/维修),后端枚举当前仅 0/1,
|
||||
* 按语义映射 可用=0 / 维修=1;「占用」待后端扩展枚举后放开(需求已登记清单)。
|
||||
*/
|
||||
val statusPhoneList = listOf(
|
||||
KeyValue("可用", "0"),
|
||||
KeyValue("维修", "1"),
|
||||
)
|
||||
|
||||
fun initOnCreate(intent: Intent) {
|
||||
id = intent.getStringExtra(Constant.Key.ID) ?: ""
|
||||
val modify = intent.getBooleanExtra(Constant.Key.DATA, false)
|
||||
@@ -42,11 +62,20 @@ class UldEditViewModel : BaseViewModel() {
|
||||
if (id.isEmpty()) {
|
||||
// 新增:直接加载航司列表
|
||||
loadCarrierList(null)
|
||||
// 手机版新增默认所在港 HFE(设计稿约定);平板无此预填,行为不变
|
||||
if (DeviceUtil.isPhone()) {
|
||||
uldBean.value = uldBean.value?.copy(airport = "HFE")
|
||||
}
|
||||
} else {
|
||||
getData()
|
||||
}
|
||||
}
|
||||
|
||||
/** 手机版状态行选中回调(copy 重新赋值以触发标签样式刷新) */
|
||||
fun onStatusSelected(statusValue: String) {
|
||||
uldBean.value = uldBean.value?.copy(status = statusValue)
|
||||
}
|
||||
|
||||
private fun getData() {
|
||||
if (id.isEmpty()) return
|
||||
launchLoadingCollect({
|
||||
@@ -72,6 +101,17 @@ class UldEditViewModel : BaseViewModel() {
|
||||
showToast("ULD编号不能为空")
|
||||
return
|
||||
}
|
||||
// 手机版按设计稿校验必填项(自重 / 入库时间);平板表单无入库时间项,保持原校验行为
|
||||
if (DeviceUtil.isPhone()) {
|
||||
if (bean.uldWeight.isBlank()) {
|
||||
showToast("自重不能为空")
|
||||
return
|
||||
}
|
||||
if (bean.checkInDate.isBlank()) {
|
||||
showToast("入库时间不能为空")
|
||||
return
|
||||
}
|
||||
}
|
||||
val isAdd = pageType.value == DetailsPageType.Add
|
||||
launchLoadingCollect({
|
||||
if (isAdd) NetApply.api.saveUld(bean.toRequestBody())
|
||||
@@ -79,7 +119,12 @@ class UldEditViewModel : BaseViewModel() {
|
||||
}) {
|
||||
onSuccess = {
|
||||
showToast(it.msg.noNull(if (isAdd) "新增成功" else "修改成功"))
|
||||
if (isAdd) uldBean.value = ULDBean() else getData()
|
||||
if (isAdd) {
|
||||
// 清空表单继续录入;手机版保持所在港默认 HFE(与进入页面时一致)
|
||||
uldBean.value = if (DeviceUtil.isPhone()) ULDBean(airport = "HFE") else ULDBean()
|
||||
} else {
|
||||
getData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,27 @@ package com.lukouguoji.aerologic.page.uld.list
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.databinding.ObservableBoolean
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.lukouguoji.aerologic.R
|
||||
import com.lukouguoji.aerologic.databinding.ActivityUldListBinding
|
||||
import com.lukouguoji.module_base.base.BaseBindingActivity
|
||||
import com.lukouguoji.module_base.common.ConstantEvent
|
||||
import com.lukouguoji.module_base.impl.FlowBus
|
||||
import com.lukouguoji.module_base.impl.observe
|
||||
import com.lukouguoji.module_base.ktx.addOnItemClickListener
|
||||
import com.lukouguoji.module_base.ktx.getLifecycleOwner
|
||||
import com.lukouguoji.module_base.ktx.showToast
|
||||
import com.lukouguoji.module_base.model.ConfirmDialogModel
|
||||
import com.lukouguoji.module_base.router.ARouterConstants
|
||||
|
||||
/**
|
||||
* ULD管理列表页。
|
||||
*
|
||||
* 布局 id 不区分设备,平板/手机由资源限定符自动选择:
|
||||
* layout-sw600dp/activity_uld_list.xml(平板) / layout/activity_uld_list.xml(手机)
|
||||
*/
|
||||
@Route(path = ARouterConstants.ACTIVITY_URL_ULD_LIST)
|
||||
class UldListActivity : BaseBindingActivity<ActivityUldListBinding, UldListViewModel>() {
|
||||
|
||||
override fun layoutId(): Int {
|
||||
@@ -19,9 +34,13 @@ class UldListActivity : BaseBindingActivity<ActivityUldListBinding, UldListViewM
|
||||
return UldListViewModel::class.java
|
||||
}
|
||||
|
||||
/** 手机版筛选弹层显隐(仅 UI 状态,不涉及业务逻辑) */
|
||||
val filterPanelVisible = ObservableBoolean(false)
|
||||
|
||||
override fun initOnCreate(savedInstanceState: Bundle?) {
|
||||
setBackArrow("ULD管理")
|
||||
binding.viewModel = viewModel
|
||||
binding.activity = this
|
||||
viewModel.pageModel.bindSmartRefreshLayout(
|
||||
binding.srl,
|
||||
binding.rv,
|
||||
@@ -29,6 +48,11 @@ class UldListActivity : BaseBindingActivity<ActivityUldListBinding, UldListViewM
|
||||
getLifecycleOwner()
|
||||
)
|
||||
binding.rv.addOnItemClickListener(viewModel)
|
||||
|
||||
// 手机版卡片勾选变化 → 刷新全选与已选统计(平板 item 无勾选框,不会触发)
|
||||
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).observe(this) {
|
||||
viewModel.onItemCheckChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -36,6 +60,46 @@ class UldListActivity : BaseBindingActivity<ActivityUldListBinding, UldListViewM
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
// ==================== 手机版专用交互(纯 UI,复用既有 ViewModel 字段) ====================
|
||||
|
||||
/** 手机版筛选弹层显隐切换 */
|
||||
fun toggleFilterPanel() {
|
||||
filterPanelVisible.set(!filterPanelVisible.get())
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机版筛选:重置
|
||||
*
|
||||
* 只清空弹层内的四个条件(入库时间 / 进港航班号 / 所属航司 / 来源),不影响顶部搜索框。
|
||||
*/
|
||||
fun resetFilter() {
|
||||
viewModel.checkInDateFilter.value = ""
|
||||
viewModel.ifNoFilter.value = ""
|
||||
viewModel.uldSuffix.value = ""
|
||||
viewModel.sourceFilter.value = ""
|
||||
}
|
||||
|
||||
/** 手机版筛选:确认 */
|
||||
fun confirmFilter() {
|
||||
filterPanelVisible.set(false)
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
/** 手机版批量删除(二次确认后逐条调用单条删除接口) */
|
||||
fun showDeleteConfirmDialog() {
|
||||
val selected = viewModel.getSelectedItems()
|
||||
if (selected.isEmpty()) {
|
||||
showToast("请选择要删除的ULD")
|
||||
return
|
||||
}
|
||||
ConfirmDialogModel(
|
||||
message = "确定要删除选中的 ${selected.size} 条ULD记录吗?",
|
||||
title = "删除确认"
|
||||
) {
|
||||
viewModel.deleteSelected()
|
||||
}.show()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun start(context: Context) {
|
||||
|
||||
@@ -4,13 +4,35 @@ import android.view.View
|
||||
import com.lukouguoji.aerologic.databinding.ItemUldListBinding
|
||||
import com.lukouguoji.module_base.base.BaseViewHolder
|
||||
import com.lukouguoji.module_base.bean.ULDBean
|
||||
import com.lukouguoji.module_base.common.ConstantEvent
|
||||
import com.lukouguoji.module_base.impl.FlowBus
|
||||
|
||||
/**
|
||||
* ULD管理列表项 ViewHolder。
|
||||
*
|
||||
* 平板 / 手机共用:布局按资源限定符切换,本类不做设备判断。
|
||||
* 平板独有 id(tv_modify / tv_delete 侧滑按钮)与手机独有 id(iv_check 勾选框)
|
||||
* 在 Binding 基类中为 @Nullable,各自判空使用。
|
||||
*/
|
||||
class UldListViewHolder(view: View) : BaseViewHolder<ULDBean, ItemUldListBinding>(view) {
|
||||
override fun onBind(item: Any?, position: Int) {
|
||||
val itemBean = getItemBean(item)!!
|
||||
binding.bean = itemBean
|
||||
|
||||
// 点卡片进详情(两端共用,走 ViewModel.onItemClick 的 R.id.ll_content 分支)
|
||||
notifyItemClick(position, binding.llContent)
|
||||
notifyItemClick(position, binding.tvModify)
|
||||
notifyItemClick(position, binding.tvDelete)
|
||||
|
||||
// 平板侧滑按钮(手机变体无此控件)
|
||||
binding.tvModify?.let { notifyItemClick(position, it) }
|
||||
binding.tvDelete?.let { notifyItemClick(position, it) }
|
||||
|
||||
// 手机版勾选框(平板变体无此控件):切换选中态并通知页面刷新全选/已选统计
|
||||
binding.ivCheck?.setOnClickListener {
|
||||
itemBean.checked.set(!itemBean.checked.get())
|
||||
FlowBus.with<String>(ConstantEvent.EVENT_CHECK_CHANGED).tryEmit("check_changed")
|
||||
binding.executePendingBindings()
|
||||
}
|
||||
|
||||
binding.executePendingBindings()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,27 @@ class UldListViewModel : BasePageViewModel() {
|
||||
val uldSuffix = MutableLiveData("")
|
||||
val uldNo = MutableLiveData("")
|
||||
|
||||
// ========== 手机版(5.5 寸手持端)专用筛选条件 ==========
|
||||
// 平板布局无对应控件,恒为空 → 不进请求参数,平板查询行为与改造前完全一致。
|
||||
// 三个入参后端暂不支持(api-doc 已核对),已登记 documents/后端接口对接问题清单.xlsx,
|
||||
// 按「以设计为准」的约定照传(后端静默忽略无副作用),后端支持后自动生效。
|
||||
val checkInDateFilter = MutableLiveData("") // 入库时间
|
||||
val ifNoFilter = MutableLiveData("") // 进港航班号
|
||||
val sourceFilter = MutableLiveData("") // 来源:空运 / 陆运
|
||||
|
||||
/** 来源下拉(本地枚举,后端字段需求已登记清单) */
|
||||
val sourceList = MutableLiveData<List<KeyValue>>(
|
||||
listOf(
|
||||
KeyValue("全部", ""),
|
||||
KeyValue("空运", "空运"),
|
||||
KeyValue("陆运", "陆运"),
|
||||
)
|
||||
)
|
||||
|
||||
// ========== 手机版批量选择状态 ==========
|
||||
val isAllChecked = MutableLiveData(false)
|
||||
val selectedCount = MutableLiveData("0")
|
||||
|
||||
// 状态下拉列表
|
||||
val statusList = MutableLiveData<List<KeyValue>>(
|
||||
listOf(
|
||||
@@ -61,16 +82,75 @@ class UldListViewModel : BasePageViewModel() {
|
||||
"status" to status.value?.ifEmpty { null },
|
||||
"uldSuffix" to uldSuffix.value?.ifEmpty { null },
|
||||
"uld" to uldNo.value?.ifEmpty { null },
|
||||
// 手机版筛选维度(平板恒空不参与);后端暂不支持这三个入参,已登记清单
|
||||
"checkInDate" to checkInDateFilter.value?.ifEmpty { null },
|
||||
"ifNo" to ifNoFilter.value?.ifEmpty { null },
|
||||
"source" to sourceFilter.value?.ifEmpty { null },
|
||||
).toRequestBody()
|
||||
)
|
||||
}) {
|
||||
onSuccess = {
|
||||
pageModel.handleListBean(it.toBaseListBean())
|
||||
pageModel.handleListBean(it.toBaseListBean()) { updateCheckAllStatus() }
|
||||
count.value = it.total
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 手机版:多选与批量删除 ==========
|
||||
|
||||
fun onItemCheckChanged() {
|
||||
updateCheckAllStatus()
|
||||
}
|
||||
|
||||
fun updateCheckAllStatus() {
|
||||
val list = pageModel.rv?.commonAdapter()?.items?.filterIsInstance<ULDBean>() ?: return
|
||||
isAllChecked.value = list.isNotEmpty() && list.all { it.checked.get() }
|
||||
selectedCount.value = list.count { it.checked.get() }.toString()
|
||||
}
|
||||
|
||||
fun toggleSelectAll() {
|
||||
val list = pageModel.rv?.commonAdapter()?.items?.filterIsInstance<ULDBean>() ?: return
|
||||
val shouldCheckAll = !(isAllChecked.value ?: false)
|
||||
list.forEach { it.checked.set(shouldCheckAll) }
|
||||
isAllChecked.value = shouldCheckAll
|
||||
selectedCount.value = (if (shouldCheckAll) list.size else 0).toString()
|
||||
pageModel.rv?.commonAdapter()?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun getSelectedItems(): List<ULDBean> =
|
||||
pageModel.rv?.commonAdapter()?.items?.filterIsInstance<ULDBean>()
|
||||
?.filter { it.checked.get() } ?: emptyList()
|
||||
|
||||
/**
|
||||
* 批量删除选中的 ULD。
|
||||
*
|
||||
* 后端仅有单条删除接口(/eqm/uld/deleteUld,批量接口需求已登记清单),
|
||||
* 此处按队列逐条串行调用,全部完成后统一刷新;中途失败即停并提示。
|
||||
*/
|
||||
fun deleteSelected() {
|
||||
val ulds = getSelectedItems().map { it.uld }
|
||||
if (ulds.isEmpty()) {
|
||||
showToast("请选择要删除的ULD")
|
||||
return
|
||||
}
|
||||
deleteQueue(ulds, 0)
|
||||
}
|
||||
|
||||
private fun deleteQueue(ulds: List<String>, index: Int) {
|
||||
if (index >= ulds.size) {
|
||||
showToast("已删除 ${ulds.size} 条")
|
||||
refresh()
|
||||
return
|
||||
}
|
||||
launchLoadingCollect({ NetApply.api.deleteUld(ulds[index]) }) {
|
||||
onSuccess = { deleteQueue(ulds, index + 1) }
|
||||
onFailed = { _, msg ->
|
||||
showToast("删除 ${ulds[index]} 失败:${msg.noNull("未知错误")}")
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onUldScanClick() {
|
||||
ScanModel.startScan(DevUtils.getTopActivity(), Constant.RequestCode.ULD)
|
||||
}
|
||||
|
||||
153
app/src/main/res/layout-sw600dp/activity_gnj_move_stash_list.xml
Normal file
153
app/src/main/res/layout-sw600dp/activity_gnj_move_stash_list.xml
Normal file
@@ -0,0 +1,153 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListViewModel" />
|
||||
|
||||
<!-- 平板不使用,仅为与手机变体保持 <variable> 一致(DataBinding 双变体强制约束) -->
|
||||
<variable
|
||||
name="activity"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity" />
|
||||
</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" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"请选择业务类型"}'
|
||||
list="@{viewModel.businessTypeList}"
|
||||
type="@{SearchLayoutType.SPINNER}"
|
||||
value="@={viewModel.businessType}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"请输入运单号"}'
|
||||
icon="@{@drawable/img_scan}"
|
||||
setInputWaybill="@{true}"
|
||||
setOnIconClickListener="@{()-> viewModel.waybillScanClick()}"
|
||||
setRefreshCallBack="@{viewModel::refresh}"
|
||||
setSearchListRefresh="@{viewModel::getWayBillList}"
|
||||
type="@{SearchLayoutType.INPUT}"
|
||||
value="@={viewModel.waybill}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"请输入承运人"}'
|
||||
type="@{SearchLayoutType.INPUT}"
|
||||
value="@={viewModel.carrier}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
style="@style/iv_search_action"
|
||||
android:onClick="@{()-> viewModel.refresh()}"
|
||||
android:src="@drawable/img_search" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingTop="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:onClick="@{()->viewModel.checkAllClick()}"
|
||||
android:text="全选" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checkIcon"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:onClick="@{()->viewModel.checkAllClick()}"
|
||||
android:src="@drawable/img_check_all" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/srl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
itemLayoutId="@{viewModel.itemLayoutId}"
|
||||
viewHolder="@{viewModel.itemViewHolder}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_gnj_move_stash_list" />
|
||||
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@color/color_bottom_layout"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text='@{"合计:"+viewModel.count+"条,总件数:"+viewModel.number+",总重量:"+viewModel.weight+"KG"}'
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="合计:3条,总件数:212,总重量:342KG" />
|
||||
|
||||
<TextView
|
||||
style="@style/tv_bottom_btn"
|
||||
android:onClick="@{()-> viewModel.moveStashClick()}"
|
||||
android:text="移库" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
231
app/src/main/res/layout-sw600dp/activity_log_detail.xml
Normal file
231
app/src/main/res/layout-sw600dp/activity_log_detail.xml
Normal file
@@ -0,0 +1,231 @@
|
||||
<?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>
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.log.detail.LogDetailViewModel" />
|
||||
</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" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
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:orientation="vertical">
|
||||
|
||||
<!-- 标题区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/bg_white_radius_top_8"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单信息"
|
||||
android:textColor="#333333"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 分割线(贯穿卡片宽度) -->
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#E7E7E7" />
|
||||
|
||||
<!-- 内容区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_white_radius_bottom_8"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单号:"
|
||||
android:textColor="#666666"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{viewModel.waybillNo}"
|
||||
android:textColor="#333333"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单类型:"
|
||||
android:textColor="#666666"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{viewModel.waybillType}"
|
||||
android:textColor="#333333"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 流转状态 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 标题区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/bg_white_radius_top_8"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="流转状态"
|
||||
android:textColor="#333333"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#E7E7E7" />
|
||||
|
||||
<!-- 内容区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_white_radius_bottom_8"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_steps"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="23dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 操作详情 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 标题区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/bg_white_radius_top_8"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="操作详情"
|
||||
android:textColor="#333333"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#E7E7E7" />
|
||||
|
||||
<!-- 内容区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_white_radius_bottom_8"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_timeline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:paddingStart="8dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
171
app/src/main/res/layout-sw600dp/activity_uld_edit.xml
Normal file
171
app/src/main/res/layout-sw600dp/activity_uld_edit.xml
Normal file
@@ -0,0 +1,171 @@
|
||||
<?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.data.layout.DataLayoutType" />
|
||||
<import type="com.lukouguoji.module_base.common.DetailsPageType" />
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.uld.edit.UldEditViewModel" />
|
||||
<!-- 平板端不使用,但两个布局变体的 <variable> 必须一致(手机变体用它做详情态编辑入口与状态选择) -->
|
||||
<variable
|
||||
name="activity"
|
||||
type="com.lukouguoji.aerologic.page.uld.edit.UldEditActivity" />
|
||||
</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="0dp"
|
||||
android:layout_weight="1"
|
||||
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">
|
||||
|
||||
<!-- Row 1: ULD编号 / 状态 / 自重 -->
|
||||
<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="@{viewModel.pageType == DetailsPageType.Add}"
|
||||
hint='@{"请输入ULD编号"}'
|
||||
title='@{"ULD编号"}'
|
||||
titleLength="@{7}"
|
||||
type="@{DataLayoutType.INPUT}"
|
||||
value='@={viewModel.uldBean.uld}' />
|
||||
|
||||
<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.statusList}"
|
||||
title='@{"状态"}'
|
||||
titleLength="@{5}"
|
||||
type="@{DataLayoutType.SPINNER}"
|
||||
value='@={viewModel.uldBean.status}' />
|
||||
|
||||
<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.uldWeight}' />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Row 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="@{viewModel.pageType != DetailsPageType.Details}"
|
||||
hint='@{"请输入最大装载容积"}'
|
||||
title='@{"最大装载容积"}'
|
||||
titleLength="@{7}"
|
||||
type="@{DataLayoutType.INPUT}"
|
||||
value='@={viewModel.uldBean.maxVolume}' />
|
||||
|
||||
<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.maxWeight}' />
|
||||
|
||||
<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.carrierList}"
|
||||
title='@{"所属航司"}'
|
||||
titleLength="@{5}"
|
||||
type="@{DataLayoutType.SPINNER}"
|
||||
value='@={viewModel.uldBean.carrier}' />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 备注(全行,多行输入) -->
|
||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||
hint='@{"请输入备注"}'
|
||||
inputHeight="@{80}"
|
||||
title='@{"备注"}'
|
||||
titleLength="@{7}"
|
||||
type="@{DataLayoutType.INPUT}"
|
||||
value='@={viewModel.uldBean.remark}' />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
visible="@{viewModel.pageType != DetailsPageType.Details}">
|
||||
|
||||
<TextView
|
||||
style="@style/tv_bottom_btn"
|
||||
android:layout_width="120dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:onClick="@{()-> viewModel.onCancelClick()}"
|
||||
android:text="取消" />
|
||||
|
||||
<TextView
|
||||
style="@style/tv_bottom_btn"
|
||||
android:layout_width="120dp"
|
||||
android:onClick="@{()-> viewModel.onSaveClick()}"
|
||||
android:text="保存" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
122
app/src/main/res/layout-sw600dp/activity_uld_list.xml
Normal file
122
app/src/main/res/layout-sw600dp/activity_uld_list.xml
Normal file
@@ -0,0 +1,122 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType" />
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.uld.list.UldListViewModel" />
|
||||
<!-- 平板端不使用,但两个布局变体的 <variable> 必须一致(手机变体用它驱动筛选弹层) -->
|
||||
<variable
|
||||
name="activity"
|
||||
type="com.lukouguoji.aerologic.page.uld.list.UldListActivity" />
|
||||
</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" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"选择状态"}'
|
||||
list="@{viewModel.statusList}"
|
||||
type="@{SearchLayoutType.SPINNER}"
|
||||
value="@={viewModel.status}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.33" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"选择所属航司"}'
|
||||
list="@{viewModel.carrierList}"
|
||||
type="@{SearchLayoutType.SPINNER}"
|
||||
value="@={viewModel.uldSuffix}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.33" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"请输入ULD编号"}'
|
||||
icon="@{@drawable/img_scan}"
|
||||
setOnIconClickListener="@{()-> viewModel.onUldScanClick()}"
|
||||
type="@{SearchLayoutType.INPUT}"
|
||||
value="@={viewModel.uldNo}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.33" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical|start"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="24dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:onClick="@{()-> viewModel.refresh()}"
|
||||
android:padding="2dp"
|
||||
android:src="@drawable/img_search" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:onClick="@{()-> viewModel.onAddClick()}"
|
||||
android:src="@drawable/img_add" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/srl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
itemLayoutId="@{viewModel.itemLayoutId}"
|
||||
viewHolder="@{viewModel.itemViewHolder}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_uld_list" />
|
||||
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@color/white"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text='@{"合计:" + viewModel.count + "条"}'
|
||||
android:textColor="@color/bottom_tool_tips_text_color"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
296
app/src/main/res/layout-sw600dp/item_gnj_move_stash_list.xml
Normal file
296
app/src/main/res/layout-sw600dp/item_gnj_move_stash_list.xml
Normal file
@@ -0,0 +1,296 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="bean"
|
||||
type="com.lukouguoji.module_base.bean.MoveStashBean" />
|
||||
|
||||
<!-- 以下两个变量平板不使用,仅为与手机变体保持 <variable> 一致(DataBinding 双变体强制约束) -->
|
||||
<variable
|
||||
name="position"
|
||||
type="Integer" />
|
||||
|
||||
<variable
|
||||
name="activity"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity" />
|
||||
</data>
|
||||
|
||||
<!-- 外层容器承载间距 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 侧滑布局 -->
|
||||
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
|
||||
android:id="@+id/swipe_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- 主列表项容器 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/ll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_item"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_icon"
|
||||
loadImage="@{bean.checked ? @drawable/img_plane_s : @drawable/img_plane}"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/img_plane" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.1"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单号:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.wbNo}"
|
||||
android:textColor="@color/colorPrimary"
|
||||
tools:text="789165431" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="件数:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text='@{bean.pc}' />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.7"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="重量:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.weight}" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="代理人:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text='@{bean.agentCode}' />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.1"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{5}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单类型:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text='@{bean.awbType}' />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.1"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="航班:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.flight}" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="始发港:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.origin}" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="起始站:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.dep}" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="目的港:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.dest}" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.1"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{5}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="品名:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.goods}" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 右侧箭头 -->
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:src="@drawable/img_pda_right" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<!-- 侧滑菜单区域 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- 编辑按钮 -->
|
||||
<TextView
|
||||
android:id="@+id/btn_edit"
|
||||
style="@style/tv_item_action"
|
||||
android:background="@color/colorPrimary"
|
||||
android:text="编辑" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</com.mcxtzhang.swipemenulib.SwipeMenuLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</layout>
|
||||
254
app/src/main/res/layout-sw600dp/item_uld_list.xml
Normal file
254
app/src/main/res/layout-sw600dp/item_uld_list.xml
Normal file
@@ -0,0 +1,254 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="bean"
|
||||
type="com.lukouguoji.module_base.bean.ULDBean" />
|
||||
</data>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/ll_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:background="@drawable/bg_item"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/img_plane"
|
||||
tools:src="@drawable/img_plane" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 第一行:ULD编号 | 状态 | 自重 | 所属航司 | 最大装载容积 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- ULD编号 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.5"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="ULD编号:"
|
||||
android:textSize="15sp"
|
||||
completeSpace="@{5}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.uld}"
|
||||
android:textColor="@color/colorPrimary"
|
||||
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="@{3}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.statusName()}"
|
||||
android:textColor='@{bean.status.equals("0") ? @color/text_green : @color/red}'
|
||||
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="@{3}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.uldWeight}"
|
||||
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.carrier}"
|
||||
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.maxVolume}"
|
||||
android:textSize="15sp" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<!-- 第二行:最大载重 | 备注 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<!-- 最大载重 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.5"
|
||||
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.maxWeight}"
|
||||
android:textSize="15sp" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<!-- 备注 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="3.6"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="备注:"
|
||||
android:textSize="15sp"
|
||||
completeSpace="@{3}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.remark}"
|
||||
android:textSize="15sp" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:src="@drawable/img_pda_right" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_modify"
|
||||
style="@style/tv_item_action"
|
||||
android:background="#8313ef"
|
||||
android:text="修改" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_delete"
|
||||
style="@style/tv_item_action"
|
||||
android:background="@color/red"
|
||||
android:text="删除" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</com.mcxtzhang.swipemenulib.SwipeMenuLayout>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
</layout>
|
||||
332
app/src/main/res/layout/activity_gnj_move_stash_detail.xml
Normal file
332
app/src/main/res/layout/activity_gnj_move_stash_detail.xml
Normal file
@@ -0,0 +1,332 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
国内进港移库「运单详情」—— 5.5 寸手持端专属页面(无平板变体)
|
||||
|
||||
对应设计稿 11_进港移库 的「运单详情」页:
|
||||
· 卡片一:蓝色表头(运单号 + 绿点 + 操作时间)+ 类型标签行(红「运单类型」+ 货物类型)
|
||||
+ 品名大字 + 两列字段网格(件数/重量/特码/代理/出运路径/承运人)
|
||||
· 卡片二:绿色表头「移库信息」+ 移库人 / 移库时间(仅已移库显示)
|
||||
· 卡片三:橙色表头「异常照片」+ 三个虚线加号槽位(点击跳拍照上传页)
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="android.view.View" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailViewModel" />
|
||||
|
||||
<variable
|
||||
name="activity"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.detail.GnjMoveStashDetailActivity" />
|
||||
</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" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:fillViewport="true"
|
||||
android:overScrollMode="never">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- ==================== 卡片一:运单基本信息 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_phone_card"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 蓝色表头:运单号 + 绿点 + 操作时间 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_phone_card_header_blue"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@{viewModel.dataBean.wbNo}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<View
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:background="@drawable/bg_phone_dot_green" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.opDate)}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- 类型标签行:运单类型(红标签) + 货物类型 -->
|
||||
<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="22dp"
|
||||
android:background="@drawable/bg_phone_tag_red"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.awbType)}"
|
||||
android:textColor="@color/phone_tag_red_text"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:background="@color/phone_divider" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.cargoType)}"
|
||||
android:textColor="@color/phone_text_body"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 品名(主字段大字) -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:text="@{viewModel.dataBean.orDash(viewModel.dataBean.goods)}"
|
||||
android:textColor="@color/phone_text_strong"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<!-- 第 1 行:件数 / 重量 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
title='@{"件数"}'
|
||||
value="@{viewModel.dataBean.orDash(viewModel.dataBean.pc)}" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_weight="1"
|
||||
title='@{"重量"}'
|
||||
value='@{viewModel.dataBean.orDash(viewModel.dataBean.weight) + " kg"}' />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 第 2 行:特码 / 代理 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
title='@{"特码"}'
|
||||
value="@{viewModel.dataBean.spCode}" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_weight="1"
|
||||
title='@{"代理"}'
|
||||
value="@{viewModel.dataBean.agentDisplay}" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 第 3 行:出运路径 / 承运人 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
title='@{"出运路径"}'
|
||||
value="@{viewModel.dataBean.range}" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_weight="1"
|
||||
title='@{"承运人"}'
|
||||
value="@{viewModel.dataBean.by1}" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ==================== 卡片二:移库信息(仅已移库显示) ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/bg_phone_card"
|
||||
android:orientation="vertical"
|
||||
android:visibility="@{viewModel.dataBean.isMoved ? View.VISIBLE : View.GONE}">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_phone_card_header_green"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="10dp"
|
||||
android:text="移库信息"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="12dp">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
title='@{"移库人"}'
|
||||
value="@{viewModel.dataBean.opId}" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_weight="1"
|
||||
title='@{"移库时间"}'
|
||||
value="@{viewModel.dataBean.opDate}" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ==================== 卡片三:异常照片 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/bg_phone_card"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_phone_card_header_orange"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="10dp"
|
||||
android:text="异常照片"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<!-- 三个虚线加号槽位(等宽正方形由 weight + 固定高实现),点击进拍照上传页 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_phone_photo_add"
|
||||
android:gravity="center"
|
||||
android:onClick="@{()-> activity.openPhoto()}"
|
||||
android:text="+"
|
||||
android:textColor="@color/phone_text_hint"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_phone_photo_add"
|
||||
android:gravity="center"
|
||||
android:onClick="@{()-> activity.openPhoto()}"
|
||||
android:text="+"
|
||||
android:textColor="@color/phone_text_hint"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_phone_photo_add"
|
||||
android:gravity="center"
|
||||
android:onClick="@{()-> activity.openPhoto()}"
|
||||
android:text="+"
|
||||
android:textColor="@color/phone_text_hint"
|
||||
android:textSize="24sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</layout>
|
||||
@@ -1,148 +1,166 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListViewModel" />
|
||||
</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" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"请选择业务类型"}'
|
||||
list="@{viewModel.businessTypeList}"
|
||||
type="@{SearchLayoutType.SPINNER}"
|
||||
value="@={viewModel.businessType}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"请输入运单号"}'
|
||||
icon="@{@drawable/img_scan}"
|
||||
setInputWaybill="@{true}"
|
||||
setOnIconClickListener="@{()-> viewModel.waybillScanClick()}"
|
||||
setRefreshCallBack="@{viewModel::refresh}"
|
||||
setSearchListRefresh="@{viewModel::getWayBillList}"
|
||||
type="@{SearchLayoutType.INPUT}"
|
||||
value="@={viewModel.waybill}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"请输入承运人"}'
|
||||
type="@{SearchLayoutType.INPUT}"
|
||||
value="@={viewModel.carrier}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ImageView
|
||||
style="@style/iv_search_action"
|
||||
android:onClick="@{()-> viewModel.refresh()}"
|
||||
android:src="@drawable/img_search" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingTop="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:onClick="@{()->viewModel.checkAllClick()}"
|
||||
android:text="全选" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checkIcon"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:onClick="@{()->viewModel.checkAllClick()}"
|
||||
android:src="@drawable/img_check_all" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/srl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
itemLayoutId="@{viewModel.itemLayoutId}"
|
||||
viewHolder="@{viewModel.itemViewHolder}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_gnj_move_stash_list" />
|
||||
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@color/color_bottom_layout"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text='@{"合计:"+viewModel.count+"条,总件数:"+viewModel.number+",总重量:"+viewModel.weight+"KG"}'
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="合计:3条,总件数:212,总重量:342KG" />
|
||||
|
||||
<TextView
|
||||
style="@style/tv_bottom_btn"
|
||||
android:onClick="@{()-> viewModel.moveStashClick()}"
|
||||
android:text="移库" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
国内进港移库 —— 5.5 寸手持端布局(平板变体见 layout-sw600dp/ 同名文件)
|
||||
|
||||
对应设计稿 documents/5.5寸手持端设计文件/11_进港移库/进港移库.html:
|
||||
· 顶部搜索框(运单号,含扫码)+ 筛选弹层(航班日期 / 航班号 / 特码 / 运单类型)
|
||||
· 「待移库 / 已移库」Tab 绑定 viewModel.moveState("0"/"1"),
|
||||
分别走 DomImpMove/search 与 DomImpMove/searchMoved 两个端点
|
||||
· 已移库 Tab 下隐藏底部操作条(与设计稿一致)
|
||||
· 卡片长按出「拍照」浮层(见 item_gnj_move_stash_list.xml)
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="android.view.View" />
|
||||
|
||||
<import type="com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayoutType" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListViewModel" />
|
||||
|
||||
<variable
|
||||
name="activity"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity" />
|
||||
</data>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/phone_page_bg">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/title_tool_bar" />
|
||||
|
||||
<!-- ==================== 搜索行 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneSearchBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
hint='@{"搜索运单号"}'
|
||||
setOnFilterClickListener="@{(v)-> activity.toggleFilterPanel()}"
|
||||
setOnScanClickListener="@{(v)-> viewModel.waybillScanClick()}"
|
||||
setRefreshCallBack="@{viewModel::refresh}"
|
||||
upperCase="@{true}"
|
||||
value="@={viewModel.waybill}" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ==================== 待移库 / 已移库 Tab ==================== -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneStatusTab
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
counts="@{viewModel.moveStateCounts}"
|
||||
setOnTabChanged="@{viewModel::refresh}"
|
||||
tabs="@{viewModel.moveStateTabs}"
|
||||
value="@={viewModel.moveState}" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="@color/phone_divider" />
|
||||
|
||||
<!-- ==================== 列表 ==================== -->
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/srl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"
|
||||
android:paddingBottom="12dp"
|
||||
itemLayoutId="@{viewModel.itemLayoutId}"
|
||||
viewHolder="@{viewModel.itemViewHolder}"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/item_gnj_move_stash_list" />
|
||||
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
|
||||
<!-- ==================== 底部操作条(已移库 Tab 下隐藏) ==================== -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneBottomBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
actionText='@{"移库"}'
|
||||
allChecked="@{viewModel.isAllChecked}"
|
||||
countText='@{"已选 " + viewModel.selectedCount + " 项"}'
|
||||
setOnActionClick="@{(v)-> viewModel.moveStashClick()}"
|
||||
setOnAllCheckClick="@{(v)-> viewModel.toggleSelectAll()}"
|
||||
android:visibility="@{`1`.equals(viewModel.moveState) ? View.GONE : View.VISIBLE}" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ==================== 筛选弹层 ==================== -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
panelVisible="@{activity.filterPanelVisible}"
|
||||
setOnConfirmClick="@{(v)-> activity.confirmFilter()}"
|
||||
setOnDismissClick="@{(v)-> activity.toggleFilterPanel()}"
|
||||
setOnResetClick="@{(v)-> activity.resetFilter()}"
|
||||
title='@{"筛选条件"}'>
|
||||
|
||||
<!-- 航班日期(入参 fdate) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
hint='@{"请选择航班日期"}'
|
||||
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}"
|
||||
value="@={viewModel.flightNo}" />
|
||||
|
||||
<!-- 特码(入参 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 字段与字典) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
hint='@{"请选择运单类型"}'
|
||||
list="@{viewModel.businessTypeList}"
|
||||
title='@{"运单类型"}'
|
||||
type="@{PhoneDataLayoutType.SPINNER}"
|
||||
value="@={viewModel.businessType}" />
|
||||
|
||||
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</layout>
|
||||
|
||||
123
app/src/main/res/layout/activity_gnj_move_stash_photo.xml
Normal file
123
app/src/main/res/layout/activity_gnj_move_stash_photo.xml
Normal file
@@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
国内进港移库「拍照上传」—— 5.5 寸手持端专属页面(无平板变体)
|
||||
|
||||
对应设计稿 11_进港移库/photo.html:
|
||||
· 白卡「运单照片上传」+ 图片预览网格(复用公共 ImageSelectNewViewHolder,末位加号槽位)
|
||||
· 底部两个按钮:拍照/上传(蓝) + 确认提交(绿)
|
||||
|
||||
提交走 DomImpMove/modify(pic/originalPic/picNumber 三字段规范)。
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.photo.GnjMoveStashPhotoViewModel" />
|
||||
</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" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:fillViewport="true"
|
||||
android:overScrollMode="never">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_phone_card"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- 标题:运单照片上传(副行展示运单号) -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单照片上传"
|
||||
android:textColor="@color/phone_text_strong"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@{viewModel.wbNo}"
|
||||
android:textColor="@color/phone_text_body"
|
||||
android:textSize="13sp"
|
||||
tools:text="78133363573" />
|
||||
|
||||
<!-- 图片预览网格(末位加号槽位,长按出删除角标) -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:overScrollMode="never"
|
||||
itemLayoutId="@{viewModel.itemLayoutId}"
|
||||
viewHolder="@{viewModel.itemViewHolder}"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="3"
|
||||
tools:itemCount="3" />
|
||||
|
||||
<!-- 底部按钮:拍照/上传(蓝) + 确认提交(绿) -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_phone_btn_primary"
|
||||
android:gravity="center"
|
||||
android:onClick="@{()-> viewModel.pickClick()}"
|
||||
android:text="拍照/上传"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_phone_btn_green"
|
||||
android:gravity="center"
|
||||
android:onClick="@{()-> viewModel.submitClick()}"
|
||||
android:text="确认提交"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</layout>
|
||||
@@ -1,6 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
操作日志详情(运单追踪)—— 手机版布局(5.5 寸手持端,最小宽度 < 600dp 时生效)
|
||||
|
||||
平板版为同名文件 layout-sw600dp/activity_log_detail.xml(横向步骤条 + 操作详情时间线),
|
||||
两者是同一布局的资源变体,DataBinding 因此生成同一个 ActivityLogDetailBinding 类。
|
||||
|
||||
变体之间必须满足:
|
||||
· DataBinding 变量完全一致:viewModel
|
||||
· 保留 Activity 引用的控件 id:ll_steps / rv_timeline
|
||||
|
||||
手机版按设计稿「运单追踪」呈现:
|
||||
· 顶卡:运单号 + 运单类型标签
|
||||
· 流转状态:竖排时间线(LogDetailActivity 手机分支 buildVerticalSteps 动态填充 ll_steps)
|
||||
· 平板的「操作详情」时间线列表在手机设计稿中不存在,rv_timeline 保留 id 但隐藏
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
@@ -12,7 +28,7 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/color_f2"
|
||||
android:background="@color/phone_page_bg"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/title_tool_bar" />
|
||||
@@ -20,208 +36,99 @@
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
android:fillViewport="true"
|
||||
android:overScrollMode="never">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
android:padding="12dp">
|
||||
|
||||
<!-- 运单信息 -->
|
||||
<!-- ==================== 运单卡片 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:background="@drawable/bg_phone_card"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- 标题区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/bg_white_radius_top_8"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单信息"
|
||||
android:textColor="#333333"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 分割线(贯穿卡片宽度) -->
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#E7E7E7" />
|
||||
|
||||
<!-- 内容区 -->
|
||||
<!-- 运单号 + 运单类型标签 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_white_radius_bottom_8"
|
||||
android:orientation="vertical">
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:orientation="horizontal">
|
||||
android:layout_weight="1"
|
||||
android:text="@{viewModel.waybillNo}"
|
||||
android:textColor="@color/phone_text_title"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="78133358743" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单号:"
|
||||
android:textColor="#666666"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{viewModel.waybillNo}"
|
||||
android:textColor="#333333"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单类型:"
|
||||
android:textColor="#666666"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{viewModel.waybillType}"
|
||||
android:textColor="#333333"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="22dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_phone_tag_indigo"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:text="@{viewModel.waybillType}"
|
||||
android:textColor="@color/phone_tag_indigo_text"
|
||||
android:textSize="12sp"
|
||||
tools:text="国际出港" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginBottom="14dp"
|
||||
android:background="@color/phone_divider" />
|
||||
|
||||
<!-- 流转状态标题 -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="流转状态"
|
||||
android:textColor="@color/phone_text_title"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<!-- 竖排时间线(Activity 手机分支动态填充) -->
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_steps"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="14dp"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<!-- 平板的操作详情时间线,手机设计稿无此区块,保留 id 但隐藏 -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_timeline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 流转状态 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
<!-- 触底提示 -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 标题区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/bg_white_radius_top_8"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="流转状态"
|
||||
android:textColor="#333333"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#E7E7E7" />
|
||||
|
||||
<!-- 内容区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_white_radius_bottom_8"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_steps"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="23dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 操作详情 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 标题区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/bg_white_radius_top_8"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="操作详情"
|
||||
android:textColor="#333333"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#E7E7E7" />
|
||||
|
||||
<!-- 内容区 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_white_radius_bottom_8"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_timeline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:paddingStart="8dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:letterSpacing="0.3"
|
||||
android:text="查询信息已触底"
|
||||
android:textColor="@color/phone_text_hint"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -1,167 +1,267 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!--
|
||||
ULD新增 / 修改 / 详情 —— 手机版布局(5.5 寸手持端,最小宽度 < 600dp 时生效)
|
||||
|
||||
平板版为同名文件 layout-sw600dp/activity_uld_edit.xml(PadDataLayoutNew 三列表单),
|
||||
两者是同一布局的资源变体,DataBinding 生成同一个 ActivityUldEditBinding 类。
|
||||
|
||||
变体之间必须满足:
|
||||
· DataBinding 变量完全一致:viewModel / activity
|
||||
→ UldEditViewModel 的加载/保存逻辑全部复用,平板端行为不受影响
|
||||
|
||||
一套布局覆盖设计稿三个页面(标题由 Activity 按 pageType 设置):
|
||||
· ULD新增(Add) :可编辑 + 底部保存;所在港默认 HFE、必填星(编号/自重/入库时间)
|
||||
· ULD修改(Modify):编号只读,其余可编辑 + 底部保存
|
||||
· ULD详情(Details):全只读右对齐 + 顶栏右侧铅笔进入修改 + 额外展示最大装载容积/最大载重
|
||||
|
||||
「来源」「状态三态(占用)」为后端暂不支持的设计稿要求,
|
||||
已登记 documents/后端接口对接问题清单.xlsx,UI 先行、后端补齐后自动生效。
|
||||
-->
|
||||
<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="android.view.View" />
|
||||
|
||||
<import type="com.lukouguoji.module_base.common.DetailsPageType" />
|
||||
|
||||
<import type="com.lukouguoji.module_base.ui.weight.phone.PhoneFormRowType" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.uld.edit.UldEditViewModel" />
|
||||
|
||||
<variable
|
||||
name="activity"
|
||||
type="com.lukouguoji.aerologic.page.uld.edit.UldEditActivity" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/color_f2"
|
||||
android:background="@color/phone_page_bg"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/title_tool_bar" />
|
||||
<!-- 标题栏 + 详情态右侧铅笔(编辑入口) -->
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include layout="@layout/title_tool_bar" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="end"
|
||||
android:onClick="@{()-> activity.onEditClick()}"
|
||||
android:padding="14dp"
|
||||
android:src="@drawable/ic_phone_edit"
|
||||
android:visibility="@{viewModel.pageType == DetailsPageType.Details ? View.VISIBLE : View.GONE}" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:fillViewport="true">
|
||||
android:fillViewport="true"
|
||||
android:overScrollMode="never">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- ==================== 表单卡片 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_white_radius_8"
|
||||
android:background="@drawable/bg_phone_card"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
android:paddingHorizontal="16dp">
|
||||
|
||||
<!-- Row 1: ULD编号 / 状态 / 自重 -->
|
||||
<!-- ULD编号:新增可输,修改/详情只读 -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
enable="@{viewModel.pageType == DetailsPageType.Add}"
|
||||
hint='@{"请输入ULD编号"}'
|
||||
required="@{viewModel.pageType == DetailsPageType.Add}"
|
||||
title='@{"ULD编号"}'
|
||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
||||
value="@={viewModel.uldBean.uld}" />
|
||||
|
||||
<!-- 状态:彩色标签 + 底部选择面板(详情态右对齐、无箭头) -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="54dp"
|
||||
android:onClick="@{()-> activity.onStatusRowClick()}"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||
android:layout_width="0dp"
|
||||
<TextView
|
||||
android:layout_width="110dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
enable="@{viewModel.pageType == DetailsPageType.Add}"
|
||||
hint='@{"请输入ULD编号"}'
|
||||
title='@{"ULD编号"}'
|
||||
titleLength="@{7}"
|
||||
type="@{DataLayoutType.INPUT}"
|
||||
value='@={viewModel.uldBean.uld}' />
|
||||
android:text="状态"
|
||||
android:textColor="@color/phone_text_label"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||
<!-- 详情态用这个占位把标签推到右侧 -->
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||
hint='@{"请选择状态"}'
|
||||
list="@{viewModel.statusList}"
|
||||
title='@{"状态"}'
|
||||
titleLength="@{5}"
|
||||
type="@{DataLayoutType.SPINNER}"
|
||||
value='@={viewModel.uldBean.status}' />
|
||||
android:visibility="@{viewModel.pageType == DetailsPageType.Details ? View.VISIBLE : View.GONE}" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="22dp"
|
||||
android:background="@{viewModel.uldBean.status.empty ? null : (viewModel.uldBean.isStatusAvailable ? @drawable/bg_phone_tag_green : (viewModel.uldBean.isStatusOccupied ? @drawable/bg_phone_tag_orange : @drawable/bg_phone_tag_red_light))}"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:text='@{viewModel.uldBean.status.empty ? "请选择状态" : viewModel.uldBean.statusPhoneName}'
|
||||
android:textColor="@{viewModel.uldBean.status.empty ? @color/phone_text_hint : (viewModel.uldBean.isStatusAvailable ? @color/phone_tag_green_text : (viewModel.uldBean.isStatusOccupied ? @color/phone_tag_orange_text : @color/phone_danger_text))}"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<!-- 编辑态右箭头 -->
|
||||
<LinearLayout
|
||||
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.uldWeight}' />
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="@{viewModel.pageType == DetailsPageType.Details ? View.GONE : View.VISIBLE}">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:src="@drawable/ic_phone_chevron_right" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Row 2: 最大装载容积 / 最大载重 / 所属航司 -->
|
||||
<LinearLayout
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="@color/phone_divider" />
|
||||
|
||||
<!-- 所属航司 -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||
hint='@{"请选择所属航司"}'
|
||||
list="@{viewModel.carrierList}"
|
||||
title='@{"所属航司"}'
|
||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}"
|
||||
value="@={viewModel.uldBean.carrier}" />
|
||||
|
||||
<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="@{7}"
|
||||
type="@{DataLayoutType.INPUT}"
|
||||
value='@={viewModel.uldBean.maxVolume}' />
|
||||
|
||||
<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.maxWeight}' />
|
||||
|
||||
<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.carrierList}"
|
||||
title='@{"所属航司"}'
|
||||
titleLength="@{5}"
|
||||
type="@{DataLayoutType.SPINNER}"
|
||||
value='@={viewModel.uldBean.carrier}' />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 备注(全行,多行输入) -->
|
||||
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayoutNew
|
||||
<!-- 自重(kg) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||
hint='@{"请输入自重"}'
|
||||
numeric="@{true}"
|
||||
required="@{viewModel.pageType == DetailsPageType.Add}"
|
||||
title='@{"自重(kg)"}'
|
||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
||||
value="@={viewModel.uldBean.uldWeight}" />
|
||||
|
||||
<!-- 最大装载容积(仅详情展示,与设计稿一致) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
title='@{"最大装载容积(m³)"}'
|
||||
type="@{PhoneFormRowType.TEXT}"
|
||||
value="@{viewModel.uldBean.maxVolume}"
|
||||
android:visibility="@{viewModel.pageType == DetailsPageType.Details ? View.VISIBLE : View.GONE}" />
|
||||
|
||||
<!-- 最大载重(仅详情展示,与设计稿一致) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
title='@{"最大载重(kg)"}'
|
||||
type="@{PhoneFormRowType.TEXT}"
|
||||
value="@{viewModel.uldBean.maxWeight}"
|
||||
android:visibility="@{viewModel.pageType == DetailsPageType.Details ? View.VISIBLE : View.GONE}" />
|
||||
|
||||
<!-- 进港航班号(绑 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}" />
|
||||
|
||||
<!-- 所在港(新增默认 HFE,由 ViewModel 手机开关预填) -->
|
||||
<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.airport}" />
|
||||
|
||||
<!-- 来源(设计稿顺序位于所在港之后;后端字段暂不支持,已登记清单,提交后端忽略) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||
hint='@{"请选择来源"}'
|
||||
list="@{viewModel.sourceEditList}"
|
||||
title='@{"来源"}'
|
||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}"
|
||||
value="@={viewModel.uldBean.source}" />
|
||||
|
||||
<!-- 入库时间(绑 checkInDate,筛选/格式语义待后端确认,已登记清单) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||
hint='@{"请输入入库时间"}'
|
||||
required="@{viewModel.pageType == DetailsPageType.Add}"
|
||||
title='@{"入库时间"}'
|
||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.DATE}"
|
||||
value="@={viewModel.uldBean.checkInDate}" />
|
||||
|
||||
<!-- 备注 -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
enable="@{viewModel.pageType != DetailsPageType.Details}"
|
||||
hint='@{"请输入备注"}'
|
||||
inputHeight="@{80}"
|
||||
title='@{"备注"}'
|
||||
titleLength="@{7}"
|
||||
type="@{DataLayoutType.INPUT}"
|
||||
value='@={viewModel.uldBean.remark}' />
|
||||
type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.INPUT}"
|
||||
value="@={viewModel.uldBean.remark}" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<LinearLayout
|
||||
<!-- ==================== 保存按钮(详情态隐藏) ==================== -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:background="@drawable/bg_phone_btn_primary"
|
||||
android:gravity="center"
|
||||
visible="@{viewModel.pageType != DetailsPageType.Details}">
|
||||
|
||||
<TextView
|
||||
style="@style/tv_bottom_btn"
|
||||
android:layout_width="120dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:onClick="@{()-> viewModel.onCancelClick()}"
|
||||
android:text="取消" />
|
||||
|
||||
<TextView
|
||||
style="@style/tv_bottom_btn"
|
||||
android:layout_width="120dp"
|
||||
android:onClick="@{()-> viewModel.onSaveClick()}"
|
||||
android:text="保存" />
|
||||
|
||||
</LinearLayout>
|
||||
android:onClick="@{()-> viewModel.onSaveClick()}"
|
||||
android:text="保存"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="@{viewModel.pageType == DetailsPageType.Details ? View.GONE : View.VISIBLE}" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</layout>
|
||||
|
||||
@@ -1,118 +1,175 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
ULD管理 —— 手机版布局(5.5 寸手持端,最小宽度 < 600dp 时生效)
|
||||
|
||||
平板版为同名文件 layout-sw600dp/activity_uld_list.xml,两者是同一布局的资源变体,
|
||||
DataBinding 因此生成同一个 ActivityUldListBinding 类。
|
||||
|
||||
变体之间必须满足:
|
||||
· DataBinding 变量完全一致:viewModel / activity
|
||||
· 保留 Activity 中引用的控件 id:srl / rv
|
||||
→ UldListViewModel 的业务逻辑与接口调用全部复用,平板端行为不受影响
|
||||
|
||||
交互差异(均复用既有 ViewModel 字段与接口):
|
||||
· 平板 3 个横排搜索条 → 手机顶部搜索框(ULD 编号)+ 底部筛选弹层
|
||||
(入库时间 / 进港航班号 / 所属航司 / 来源;前两项与来源的筛选入参后端暂不支持,
|
||||
已登记 documents/后端接口对接问题清单.xlsx,后端支持后自动生效)
|
||||
· 平板右上「新增」按钮 → 手机右下角浮动 + 按钮
|
||||
· 平板侧滑单条删除 → 手机底部操作条(全选 + 已选统计 + 批量删除)
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<import type="com.lukouguoji.module_base.ui.weight.search.layout.SearchLayoutType" />
|
||||
|
||||
<import type="com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayoutType" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.lukouguoji.aerologic.page.uld.list.UldListViewModel" />
|
||||
|
||||
<variable
|
||||
name="activity"
|
||||
type="com.lukouguoji.aerologic.page.uld.list.UldListActivity" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/color_f2"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/title_tool_bar" />
|
||||
android:background="@color/phone_page_bg">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"选择状态"}'
|
||||
list="@{viewModel.statusList}"
|
||||
type="@{SearchLayoutType.SPINNER}"
|
||||
value="@={viewModel.status}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.33" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"选择所属航司"}'
|
||||
list="@{viewModel.carrierList}"
|
||||
type="@{SearchLayoutType.SPINNER}"
|
||||
value="@={viewModel.uldSuffix}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.33" />
|
||||
|
||||
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
|
||||
hint='@{"请输入ULD编号"}'
|
||||
icon="@{@drawable/img_scan}"
|
||||
setOnIconClickListener="@{()-> viewModel.onUldScanClick()}"
|
||||
type="@{SearchLayoutType.INPUT}"
|
||||
value="@={viewModel.uldNo}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.33" />
|
||||
<include layout="@layout/title_tool_bar" />
|
||||
|
||||
<!-- ==================== 搜索行 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical|start"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="24dp">
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:onClick="@{()-> viewModel.refresh()}"
|
||||
android:padding="2dp"
|
||||
android:src="@drawable/img_search" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:onClick="@{()-> viewModel.onAddClick()}"
|
||||
android:src="@drawable/img_add" />
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneSearchBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
hint='@{"搜索ULD编号"}'
|
||||
setOnFilterClickListener="@{(v)-> activity.toggleFilterPanel()}"
|
||||
setOnScanClickListener="@{(v)-> viewModel.onUldScanClick()}"
|
||||
setRefreshCallBack="@{viewModel::refresh}"
|
||||
upperCase="@{true}"
|
||||
value="@={viewModel.uldNo}" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/srl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
itemLayoutId="@{viewModel.itemLayoutId}"
|
||||
viewHolder="@{viewModel.itemViewHolder}"
|
||||
<!-- ==================== 列表 ==================== -->
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/srl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_uld_list" />
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"
|
||||
android:paddingBottom="80dp"
|
||||
itemLayoutId="@{viewModel.itemLayoutId}"
|
||||
viewHolder="@{viewModel.itemViewHolder}"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="4"
|
||||
tools:listitem="@layout/item_uld_list" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@color/white"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="15dp">
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text='@{"合计:" + viewModel.count + "条"}'
|
||||
android:textColor="@color/bottom_tool_tips_text_color"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
<!-- ==================== 底部操作条:全选 + 已选统计 + 批量删除 ==================== -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneBottomBar
|
||||
actionDanger="@{true}"
|
||||
actionText='@{"删除"}'
|
||||
allChecked="@{viewModel.isAllChecked}"
|
||||
countText='@{"已选 " + viewModel.selectedCount + " / " + viewModel.count + " 项"}'
|
||||
setOnActionClick="@{(v)-> activity.showDeleteConfirmDialog()}"
|
||||
setOnAllCheckClick="@{(v)-> viewModel.toggleSelectAll()}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<!-- ==================== 右下角浮动新增按钮 ==================== -->
|
||||
<TextView
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="92dp"
|
||||
android:background="@drawable/bg_phone_fab"
|
||||
android:elevation="6dp"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:onClick="@{()-> viewModel.onAddClick()}"
|
||||
android:text="+"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="32sp" />
|
||||
|
||||
<!-- ==================== 筛选弹层 ==================== -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:elevation="8dp"
|
||||
panelVisible="@{activity.filterPanelVisible}"
|
||||
setOnConfirmClick="@{(v)-> activity.confirmFilter()}"
|
||||
setOnDismissClick="@{(v)-> activity.toggleFilterPanel()}"
|
||||
setOnResetClick="@{(v)-> activity.resetFilter()}"
|
||||
title='@{"筛选条件"}'>
|
||||
|
||||
<!-- 入库时间(后端筛选入参暂不支持,已登记清单) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
hint='@{"请输入入库时间"}'
|
||||
title='@{"入库时间"}'
|
||||
type="@{PhoneDataLayoutType.DATE}"
|
||||
value="@={viewModel.checkInDateFilter}" />
|
||||
|
||||
<!-- 进港航班号(后端筛选入参暂不支持,已登记清单) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
hint='@{"请输入进港航班号"}'
|
||||
title='@{"进港航班号"}'
|
||||
type="@{PhoneDataLayoutType.INPUT}"
|
||||
value="@={viewModel.ifNoFilter}" />
|
||||
|
||||
<!-- 所属航司(后端入参 uldSuffix,真实生效) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
hint='@{"请选择所属航司"}'
|
||||
list="@{viewModel.carrierList}"
|
||||
title='@{"所属航司"}'
|
||||
type="@{PhoneDataLayoutType.SPINNER}"
|
||||
value="@={viewModel.uldSuffix}" />
|
||||
|
||||
<!-- 来源(后端字段暂不支持,已登记清单) -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
hint='@{"请选择来源"}'
|
||||
list="@{viewModel.sourceList}"
|
||||
title='@{"来源"}'
|
||||
type="@{PhoneDataLayoutType.SPINNER}"
|
||||
value="@={viewModel.sourceFilter}" />
|
||||
|
||||
</com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</layout>
|
||||
|
||||
@@ -1,287 +1,227 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
国内进港移库 —— 列表项 5.5 寸手持端布局(平板变体见 layout-sw600dp/ 同名文件)
|
||||
|
||||
一套布局覆盖设计稿两种卡片形态,按 bean.isMoved 切换(载入时按端点打标):
|
||||
· 待移库:圆形勾选框 + 蓝色运单号 + 橙色「待移库」标签
|
||||
· 已移库:无勾选框 + 深色运单号 + 绿色对勾「已移库」
|
||||
长按卡片出「拍照」浮层(跳拍照上传页),点浮层空白处收起。
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="android.view.View" />
|
||||
|
||||
<variable
|
||||
name="bean"
|
||||
type="com.lukouguoji.module_base.bean.MoveStashBean" />
|
||||
|
||||
<variable
|
||||
name="position"
|
||||
type="Integer" />
|
||||
|
||||
<variable
|
||||
name="activity"
|
||||
type="com.lukouguoji.aerologic.page.gnj.move.stash.list.GnjMoveStashListActivity" />
|
||||
</data>
|
||||
|
||||
<!-- 外层容器承载间距 -->
|
||||
<LinearLayout
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginTop="12dp">
|
||||
|
||||
<!-- 侧滑布局 -->
|
||||
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
|
||||
android:id="@+id/swipe_menu"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_phone_card"
|
||||
android:onLongClick="@{()-> activity.showCardActionPhone(bean)}"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- 主列表项容器 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/ll"
|
||||
<!-- ==================== 卡片头部:勾选框 + 运单号 + 状态 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_item"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- 圆形勾选框(已移库的记录不可勾选,隐藏) -->
|
||||
<ImageView
|
||||
android:id="@+id/iv_icon"
|
||||
loadImage="@{bean.checked ? @drawable/img_plane_s : @drawable/img_plane}"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/img_plane" />
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:src="@drawable/ic_phone_check_round_unchecked"
|
||||
android:visibility="@{bean.isMoved ? View.GONE : View.VISIBLE}"
|
||||
loadImage="@{bean.checked.get() ? @drawable/ic_phone_check_round_checked : @drawable/ic_phone_check_round_unchecked}" />
|
||||
|
||||
<LinearLayout
|
||||
<!-- 运单号 -->
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
android:text="@{bean.wbNo}"
|
||||
android:textColor="@{bean.isMoved ? @color/phone_text_title : @color/phone_primary}"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<!-- 待移库:橙色状态标签 -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_phone_tag_orange"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:text="待移库"
|
||||
android:textColor="@color/phone_tag_orange_text"
|
||||
android:textSize="11sp"
|
||||
android:visibility="@{bean.isMoved ? View.GONE : View.VISIBLE}" />
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.1"
|
||||
android:gravity="center_vertical">
|
||||
<!-- 已移库:对勾 + 状态文字 -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="@{bean.isMoved ? View.VISIBLE : View.GONE}">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单号:" />
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:src="@drawable/ic_phone_check_circle" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.wbNo}"
|
||||
android:textColor="@color/colorPrimary"
|
||||
tools:text="789165431" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="件数:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text='@{bean.pc}' />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.7"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="重量:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.weight}" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="代理人:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text='@{bean.agentCode}' />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.1"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{5}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="运单类型:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text='@{bean.awbType}' />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.1"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="航班:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.flight}" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="始发港:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.origin}" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="起始站:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.dep}" />
|
||||
|
||||
</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
|
||||
completeSpace="@{4}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="目的港:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.dest}" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.1"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
completeSpace="@{5}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="品名:" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.goods}" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
android:layout_marginStart="4dp"
|
||||
android:text="已移库"
|
||||
android:textColor="@color/phone_text_title"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 右侧箭头 -->
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:src="@drawable/img_pda_right" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<!-- 侧滑菜单区域 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
<!-- ==================== 航班信息行 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- 编辑按钮 -->
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:src="@drawable/ic_phone_airplane" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btn_edit"
|
||||
style="@style/tv_item_action"
|
||||
android:background="@color/colorPrimary"
|
||||
android:text="编辑" />
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text='@{"航班信息:" + bean.orDash(bean.flight)}'
|
||||
android:textColor="@color/phone_text_body"
|
||||
android:textSize="13sp"
|
||||
tools:text="航班信息:20260709/MU2311" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
</LinearLayout>
|
||||
|
||||
</com.mcxtzhang.swipemenulib.SwipeMenuLayout>
|
||||
<!-- ==================== 统计框:特码 / 件数 / 重量 ==================== -->
|
||||
<com.lukouguoji.module_base.ui.weight.phone.PhoneStatBox
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
label1='@{"特码"}'
|
||||
label2='@{"件数"}'
|
||||
label3='@{"重量"}'
|
||||
value1="@{bean.orDash(bean.spCode)}"
|
||||
value2='@{bean.orDash(bean.pc) + "件"}'
|
||||
value3='@{bean.orDash(bean.weight) + "kg"}' />
|
||||
|
||||
</LinearLayout>
|
||||
<!-- ==================== 出运路径 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:src="@drawable/ic_phone_location" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_weight="1"
|
||||
android:text='@{"出运路径:" + bean.orDash(bean.range)}'
|
||||
android:textColor="@color/phone_text_body"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ==================== 查看详情 ==================== -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/phone_stat_bg" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_detail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical|end"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="查看详情"
|
||||
android:textColor="@color/phone_primary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:src="@drawable/ic_phone_chevron_right" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ==================== 长按操作浮层:拍照 ==================== -->
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#66000000"
|
||||
android:clickable="true"
|
||||
android:onClick="@{()-> activity.hideCardActionPhone(bean)}"
|
||||
android:visibility="@{bean.actionMenuVisible ? View.VISIBLE : View.GONE}">
|
||||
|
||||
<TextView
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/bg_phone_fab_purple"
|
||||
android:gravity="center"
|
||||
android:onClick="@{()-> activity.openPhoto(bean)}"
|
||||
android:text="拍照"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</layout>
|
||||
|
||||
@@ -1,254 +1,148 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
ULD管理 —— 列表项手机版布局(5.5 寸手持端,最小宽度 < 600dp 时生效)
|
||||
|
||||
平板版为同名文件 layout-sw600dp/item_uld_list.xml(SwipeMenuLayout 侧滑修改/删除),
|
||||
两者是同一布局的资源变体,DataBinding 生成同一个 ItemUldListBinding 类。
|
||||
|
||||
变体之间必须满足:
|
||||
· DataBinding 变量完全一致:bean
|
||||
· 保留 ViewHolder / ViewModel 引用的 id:ll_content(点卡片进详情,复用平板 onItemClick 链路)
|
||||
· 平板独有 id(tv_modify / tv_delete / iv_icon)与手机独有 id(iv_check)
|
||||
在 Binding 基类中自动为 @Nullable,ViewHolder 判空使用
|
||||
|
||||
状态标签三色(设计稿):可用=浅绿 / 占用=浅橙 / 维修=浅红。
|
||||
后端当前仅两态(0 可用 / 1 维修),「占用」为后端扩展枚举后的预留,见 ULDBean.statusPhoneName。
|
||||
「来源」字段后端暂无(恒空显示 "-"),已登记 documents/后端接口对接问题清单.xlsx。
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="android.view.View" />
|
||||
|
||||
<variable
|
||||
name="bean"
|
||||
type="com.lukouguoji.module_base.bean.ULDBean" />
|
||||
</data>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/ll_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="@drawable/bg_phone_card"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
|
||||
<!-- ==================== 头行:勾选框 + ULD编号 + 状态标签 + 箭头 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<ImageView
|
||||
android:id="@+id/iv_check"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:src="@drawable/ic_phone_check_round_unchecked"
|
||||
loadImage="@{bean.checked.get() ? @drawable/ic_phone_check_round_checked : @drawable/ic_phone_check_round_unchecked}" />
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/ll_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:background="@drawable/bg_item"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@{bean.uld}"
|
||||
android:textColor="@color/phone_text_title"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/img_plane"
|
||||
tools:src="@drawable/img_plane" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 第一行:ULD编号 | 状态 | 自重 | 所属航司 | 最大装载容积 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- ULD编号 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.5"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="ULD编号:"
|
||||
android:textSize="15sp"
|
||||
completeSpace="@{5}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.uld}"
|
||||
android:textColor="@color/colorPrimary"
|
||||
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="@{3}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.statusName()}"
|
||||
android:textColor='@{bean.status.equals("0") ? @color/text_green : @color/red}'
|
||||
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="@{3}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.uldWeight}"
|
||||
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.carrier}"
|
||||
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.maxVolume}"
|
||||
android:textSize="15sp" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<!-- 第二行:最大载重 | 备注 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<!-- 最大载重 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.5"
|
||||
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.maxWeight}"
|
||||
android:textSize="15sp" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<!-- 备注 -->
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="3.6"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="备注:"
|
||||
android:textSize="15sp"
|
||||
completeSpace="@{3}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{bean.remark}"
|
||||
android:textSize="15sp" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:src="@drawable/img_pda_right" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
<!-- 状态标签:可用(绿) / 占用(橙) / 维修(红,浅红底红字) -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@{bean.isStatusAvailable ? @drawable/bg_phone_tag_green : (bean.isStatusOccupied ? @drawable/bg_phone_tag_orange : @drawable/bg_phone_tag_red_light)}"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:text="@{bean.statusPhoneName}"
|
||||
android:textColor="@{bean.isStatusAvailable ? @color/phone_tag_green_text : (bean.isStatusOccupied ? @color/phone_tag_orange_text : @color/phone_danger_text)}"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_modify"
|
||||
style="@style/tv_item_action"
|
||||
android:background="#8313ef"
|
||||
android:text="修改" />
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="6dp"
|
||||
android:src="@drawable/ic_phone_chevron_right" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_delete"
|
||||
style="@style/tv_item_action"
|
||||
android:background="@color/red"
|
||||
android:text="删除" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
<!-- ==================== 第二行:所属航司 / 来源 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
</com.mcxtzhang.swipemenulib.SwipeMenuLayout>
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:src="@drawable/ic_phone_airplane" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_weight="1"
|
||||
android:text='@{"所属航司:" + (bean.carrier.empty ? "-" : bean.carrier)}'
|
||||
android:textColor="@color/phone_text_body"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:src="@drawable/ic_phone_location" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_weight="1"
|
||||
android:text='@{"来源:" + (bean.source.empty ? "-" : bean.source)}'
|
||||
android:textColor="@color/phone_text_body"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ==================== 第三行:入库时间 ==================== -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:src="@drawable/ic_phone_calendar" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_weight="1"
|
||||
android:text='@{"入库时间:" + (bean.checkInDate.empty ? "-" : bean.checkInDate)}'
|
||||
android:textColor="@color/phone_text_body"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</layout>
|
||||
|
||||
55
documents/5.5寸手持端-后端字段补充清单.md
Normal file
55
documents/5.5寸手持端-后端字段补充清单.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# 5.5 寸手持端适配 —— 后端接口补充清单
|
||||
|
||||
适配手机端时,设计稿要求展示/筛选的部分内容,现有接口没有提供或未生效。
|
||||
以下为**实机联调(内网真实数据)验证过**的结论,客户端侧已按设计稿把 UI 与参数都做好,
|
||||
后端补齐后即可直接生效,客户端基本无需再改。
|
||||
|
||||
整理时间:2026-07-29 验证环境:内网 `192.168.1.250:8093`,账号 ADMIN
|
||||
|
||||
---
|
||||
|
||||
## 一、参数已发送但后端未生效(客户端已就绪,等后端支持)
|
||||
|
||||
| 接口 | 参数 | 用途 | 实测现象 |
|
||||
|------|------|------|----------|
|
||||
| `IntExpOutHandover/pageQuery`<br>`IntExpOutHandover/pageQueryTotal` | `hoState`<br>("0" 待交接 / "1" 已交接) | 出库交接页「待交接 / 已交接」Tab 过滤与角标计数 | 请求体确认携带 `"hoState":"0"` / `"1"`,但两个 Tab 返回**完全相同**的数据;`pageQueryTotal` 的 `wbNumber` 也不随之变化 |
|
||||
| `IntExpMove/pageQuery`<br>`IntExpMove/pageQueryTotal` | `spCode`<br>(特码,如 "PER") | 出港移库页筛选弹层「特码」 | 请求体确认携带 `"spCode":"PER"`,但返回结果中仍含特码为 `PEF` 的记录,角标计数也无变化 |
|
||||
|
||||
> 对照参考:同一接口的 `moveState`、`by1`(承运人)、`awbType`(运单类型)**均正常生效**
|
||||
> (实测承运人填 MU 后待移库数量 67 → 60),因此可以排除客户端传参方式的问题。
|
||||
>
|
||||
> 若贵方使用的参数名与上表不同,请告知正确名称,客户端改一处常量即可。
|
||||
|
||||
---
|
||||
|
||||
## 二、接口未返回、设计稿需要展示的字段
|
||||
|
||||
以下字段目前接口完全不返回,或恒为 `null`。客户端已按设计稿保留完整 UI 结构,
|
||||
数据为空时统一占位 `-`,后端补齐后自动显示。
|
||||
|
||||
### 2.1 `IntExpMove/pageQuery`(出港移库)
|
||||
|
||||
| 设计稿位置 | 需要的字段 | 当前状况 |
|
||||
|------------|-----------|----------|
|
||||
| 列表卡片第二行「航班信息:20260709/MU2311」 | 航班日期 + 航班号(如 `fdate` / `fno`) | **接口无任何航班相关字段** |
|
||||
| 详情页「移库信息」卡片 —— 移库人 | 移库人**姓名**(现只有 `opId`) | `opId` 恒为 `null`,且即便有值也是 ID 而非姓名 |
|
||||
| 详情页「移库信息」卡片 —— 移库时间 | `opdate` | 恒为 `null`,**已移库记录也是 null** |
|
||||
| 详情页蓝色表头右侧时间戳 | 同上 `opdate` | 同上 |
|
||||
| 详情页类型行右侧「普通货物运输」 | `businessType` 或 `cargoType` | 两者均恒为 `null` |
|
||||
|
||||
### 2.2 说明
|
||||
|
||||
- 出库交接页的详情所需字段(板型、库位、复磅人/时间等)接口均已返回,无需补充。
|
||||
- 移库详情页红色标签绑的是 `awbTypeName`(真实值形如「国际出港(国际直航)」),已正常。
|
||||
设计稿上写的「国际进港」经判断为设计稿笔误(该页属国际出港),未按字面实现。
|
||||
|
||||
---
|
||||
|
||||
## 三、附:字典接口调用口径(客户端已按此对齐,仅作备忘)
|
||||
|
||||
`typeCode/getSpecialCodeByParam`(特码字典):
|
||||
|
||||
- `flag=1`(国际)+ `ieFlag=""`(**留空**)→ 正常返回 RXE/PER/PEM/PEF/LDP/GJJ/DGR 等
|
||||
- `flag=1` + `ieFlag="E"` → 返回 `"data": []` 空数组
|
||||
|
||||
国际出港各页面统一使用 `ieFlag=""`。如该行为非预期,请后端确认 `ieFlag` 的取值约定。
|
||||
195
documents/5.5寸手持端设计文件/00_全部入口总览.html
Normal file
195
documents/5.5寸手持端设计文件/00_全部入口总览.html
Normal file
@@ -0,0 +1,195 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>5.5 寸手持端设计稿 · 全部入口总览</title>
|
||||
<style>
|
||||
:root {
|
||||
--cols: 4;
|
||||
--scale: 1;
|
||||
--pw: 430px;
|
||||
--ph: 890px;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0 32px 48px;
|
||||
background: #EEF1F6;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Arial, sans-serif;
|
||||
color: #1F2937;
|
||||
}
|
||||
header {
|
||||
padding: 32px 0 20px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
letter-spacing: .5px;
|
||||
}
|
||||
h1 small {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #6B7280;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
background: #fff;
|
||||
border: 1px solid #E3E8F0;
|
||||
border-radius: 10px;
|
||||
padding: 10px 16px;
|
||||
font-size: 13px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
}
|
||||
.toolbar label { color: #6B7280; margin-right: 6px; }
|
||||
.toolbar select {
|
||||
font-size: 13px;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #D8DEE9;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
color: #1F2937;
|
||||
}
|
||||
.toolbar button {
|
||||
font-size: 13px;
|
||||
padding: 5px 12px;
|
||||
border: 1px solid #2563EB;
|
||||
background: #2563EB;
|
||||
color: #fff;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.toolbar button:active { opacity: .8; }
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--cols), max-content);
|
||||
gap: 28px 24px;
|
||||
justify-content: start;
|
||||
}
|
||||
.card {
|
||||
background: #fff;
|
||||
border: 1px solid #E3E8F0;
|
||||
border-radius: 14px;
|
||||
padding: 12px 12px 14px;
|
||||
box-shadow: 0 2px 10px rgba(17,24,39,.06);
|
||||
width: calc(var(--pw) * var(--scale) + 24px);
|
||||
}
|
||||
.cap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0 2px 10px;
|
||||
}
|
||||
.no {
|
||||
flex: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 6px;
|
||||
background: #2563EB;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.name { font-size: 15px; font-weight: 600; }
|
||||
.tag {
|
||||
margin-left: auto;
|
||||
font-size: 11px;
|
||||
color: #2563EB;
|
||||
background: #EAF1FE;
|
||||
border-radius: 5px;
|
||||
padding: 2px 7px;
|
||||
}
|
||||
.frame {
|
||||
width: calc(var(--pw) * var(--scale));
|
||||
height: calc(var(--ph) * var(--scale));
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
background: #E5E7EB;
|
||||
}
|
||||
.frame iframe {
|
||||
width: var(--pw);
|
||||
height: var(--ph);
|
||||
border: 0;
|
||||
display: block;
|
||||
transform: scale(var(--scale));
|
||||
transform-origin: top left;
|
||||
}
|
||||
body.clean .toolbar { display: none; }
|
||||
body.clean header { padding-bottom: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>5.5 寸手持端设计稿 · 全部入口总览
|
||||
<small>共 11 个业务入口 · 单机画布 390 × 844</small>
|
||||
</h1>
|
||||
<div class="toolbar">
|
||||
<span><label>列数</label>
|
||||
<select id="cols" onchange="setVar('--cols', this.value)">
|
||||
<option>2</option><option>3</option><option selected>4</option><option>5</option><option>6</option>
|
||||
</select>
|
||||
</span>
|
||||
<span><label>缩放</label>
|
||||
<select id="scale" onchange="setVar('--scale', this.value)">
|
||||
<option value="1">100%</option>
|
||||
<option value="0.85">85%</option>
|
||||
<option value="0.75">75%</option>
|
||||
<option value="0.6">60%</option>
|
||||
<option value="0.5">50%</option>
|
||||
</select>
|
||||
</span>
|
||||
<button onclick="document.body.classList.toggle('clean')">隐藏工具栏</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="grid" id="grid"></div>
|
||||
|
||||
<script>
|
||||
const PAGES = [
|
||||
['01', 'ULD 管理', '综合管理', '01_ULD管理/ULD管理.html'],
|
||||
['02', '出库交接', '国际出港', '02_出库交接/出库交接.html'],
|
||||
['03', '出港计重', '国际出港', '03_出港计重/main.html'],
|
||||
['04', '出港运抵', '国际出港', '04_出港运抵/main.html'],
|
||||
['05', '出港移库', '国际出港', '05_出港移库/出港移库.html'],
|
||||
['06', '出港查询', '国际出港', '06_出港查询/出港查询.html'],
|
||||
['07', '出港仓库', '国际出港', '07_出港仓库/出港仓库.html'],
|
||||
['08', '进港仓库', '国际进港', '08_进港仓库/进港仓库.html'],
|
||||
['09', '提取出库', '国际进港', '09_提取出库/提取出库.html'],
|
||||
['10', '进港查询', '国际进港', '10_进港查询/进港查询.html'],
|
||||
['11', '进港移库', '国际进港', '11_进港移库/进港移库.html'],
|
||||
];
|
||||
|
||||
document.getElementById('grid').innerHTML = PAGES.map(([no, name, menu, src]) => `
|
||||
<div class="card">
|
||||
<div class="cap">
|
||||
<span class="no">${no}</span>
|
||||
<span class="name">${name}</span>
|
||||
<span class="tag">${menu}</span>
|
||||
</div>
|
||||
<div class="frame">
|
||||
<iframe src="${src}" loading="eager" scrolling="no"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
function setVar(k, v) {
|
||||
document.documentElement.style.setProperty(k, v);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
650
documents/5.5寸手持端设计文件/01_ULD管理/ULD管理.html
Normal file
650
documents/5.5寸手持端设计文件/01_ULD管理/ULD管理.html
Normal file
@@ -0,0 +1,650 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>ULD管理</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #F3F4F6;
|
||||
font-size: 13px;
|
||||
}
|
||||
.safe-bottom {
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.custom-checkbox-round {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
border-radius: 9999px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 2px solid #D1D5DB;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.custom-checkbox-round:checked {
|
||||
background-color: #0D6EFD;
|
||||
border-color: #0D6EFD;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z'/%3E%3C/svg%3E");
|
||||
background-size: 14px 14px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.btn-active:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-200 min-h-screen flex items-center justify-center p-4">
|
||||
<div class="relative w-[390px] h-[844px] bg-[#1a1a1a] rounded-[55px] shadow-2xl border-[6px] border-[#333] flex flex-col overflow-hidden">
|
||||
<div class="flex-1 bg-white mt-0 mb-1 mx-1 rounded-[46px] overflow-hidden flex flex-col relative">
|
||||
<div class="bg-[#2563EB] text-white h-11 px-7 flex items-end pb-2 justify-between text-[14px] font-medium z-50">
|
||||
<span>09:41</span>
|
||||
<div class="flex items-center gap-1.5 pb-0.5">
|
||||
<iconify-icon icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col overflow-hidden relative bg-gray-100">
|
||||
<header class="relative bg-[#2563EB] text-white flex items-center h-12 px-3 sticky top-0 z-50">
|
||||
<div class="flex items-center btn-active" onclick="alert('返回首页')">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-medium text-white -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="absolute left-1/2 -translate-x-1/2 text-lg font-bold text-white">ULD管理</h1>
|
||||
</header>
|
||||
<section class="px-4 pt-4 pb-3 flex-none">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-[12px] h-10 px-3 gap-2 flex-1 shadow-sm">
|
||||
<iconify-icon class="text-gray-500 text-[14px] text-lg flex-none" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-700 placeholder-gray-400 outline-none border-none rounded-lg px-2" placeholder="搜索ULD编号" type="text"/>
|
||||
<button class="flex-none text-blue-500">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="toggleFilterDrawer(true)">
|
||||
<iconify-icon class="text-lg" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<main class="flex-1 overflow-y-auto px-4 pt-3 pb-[80px] no-scrollbar">
|
||||
<!-- 卡片1 -->
|
||||
<div class="bg-white rounded-xl shadow-sm mb-3 p-4 relative cursor-pointer" onclick="showUldDetail('PMC78133363396', '占用', 'CA', '150', '12.5', '6800', 'CA1111', 'PEK', 'B区-05货位', '--')">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="event.stopPropagation(); handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<span class="text-[15px] font-bold text-[#1F2937]">PMC78133363396</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-[#FFF3E0] text-[#E67722] text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">占用</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right" width="14"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2 text-gray-500 text-[13px]">
|
||||
<div class="grid grid-cols-2 gap-x-3">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:office-building-outline"></iconify-icon>
|
||||
<span>所属航司:CA</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>来源:空运</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:calendar-clock"></iconify-icon>
|
||||
<span>入库时间:2026-07-20 15:59:20</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片2 -->
|
||||
<div class="bg-white rounded-xl shadow-sm mb-3 p-4 relative cursor-pointer" onclick="showUldDetail('PMC78133363397', '可用', 'CA', '120', '10.0', '5500', 'CA2222', 'CAN', 'C区-12货位', '--')">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="event.stopPropagation(); handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<span class="text-[15px] font-bold text-[#1F2937]">PMC78133363397</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">可用</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right" width="14"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2 text-gray-500 text-[13px]">
|
||||
<div class="grid grid-cols-2 gap-x-3">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:office-building-outline"></iconify-icon>
|
||||
<span>所属航司:CA</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>来源:空运</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:calendar-clock"></iconify-icon>
|
||||
<span>入库时间:2026-07-20 15:59:20</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片3 -->
|
||||
<div class="bg-white rounded-xl shadow-sm mb-3 p-4 relative cursor-pointer" onclick="showUldDetail('PMC78133363398', '维修', 'CA', '0', '0.0', '0', 'CA3333', 'PVG', '维修区', '维修中')">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="event.stopPropagation(); handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<span class="text-[15px] font-bold text-[#1F2937]">PMC78133363398</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-red-50 text-red-600 text-[11px] px-2 py-0.5 rounded font-medium">维修</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right" width="14"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2 text-gray-500 text-[13px]">
|
||||
<div class="grid grid-cols-2 gap-x-3">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:office-building-outline"></iconify-icon>
|
||||
<span>所属航司:CA</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>来源:陆运</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:calendar-clock"></iconify-icon>
|
||||
<span>入库时间:2026-07-20 15:59:20</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片4 -->
|
||||
<div class="bg-white rounded-xl shadow-sm mb-3 p-4 relative cursor-pointer" onclick="showUldDetail('PMC78133363399', '占用', 'CA', '280', '20.0', '8500', 'CA4444', 'CTU', 'A区-08货位', '--')">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="event.stopPropagation(); handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<span class="text-[15px] font-bold text-[#1F2937]">PMC78133363399</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-[#FFF3E0] text-[#E67722] text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">占用</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right" width="14"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2 text-gray-500 text-[13px]">
|
||||
<div class="grid grid-cols-2 gap-x-3">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:office-building-outline"></iconify-icon>
|
||||
<span>所属航司:CA</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>来源:空运</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:calendar-clock"></iconify-icon>
|
||||
<span>入库时间:2026-07-20 15:59:20</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div class="absolute bottom-0 w-full bg-white border-t border-gray-200 z-40 flex flex-col pt-3 pb-3" id="bottom-toolbar" style="display: flex;">
|
||||
<div class="absolute right-4 -top-[73px] z-50">
|
||||
<button class="bg-[#2563EB] text-white w-11 h-11 rounded-full flex items-center justify-center shadow-lg shadow-blue-500/40" onclick="showAddDialog()">
|
||||
<iconify-icon icon="mdi:plus" width="24"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="px-4 flex items-center w-full">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="custom-checkbox-round flex-none" id="select-all-cb" onclick="toggleSelectAll(this.checked)" type="checkbox"/>
|
||||
<label class="text-[13px] text-[#1F2937] font-medium cursor-pointer" for="select-all-cb">全选</label>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-[12px] text-[#9CA3AF]">已选 <span id="selected-count">0 / 4</span> 项</span>
|
||||
<button class="bg-[#FDEDED] text-[#DC2626] text-[15px] font-bold px-5 py-2 rounded-[10px] flex items-center gap-1.5 btn-active" onclick="handleBatchDelete()">
|
||||
<iconify-icon class="text-base" icon="mdi:delete-outline"></iconify-icon>
|
||||
<span>删除</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 新增弹窗 -->
|
||||
<div class="absolute inset-0 bg-gray-100 z-[60] flex flex-col hidden" id="addUldOverlay">
|
||||
<div class="bg-[#2563EB] text-white h-11 px-7 flex items-end pb-2 justify-between text-[14px] font-medium z-50 flex-none">
|
||||
<span>09:41</span>
|
||||
<div class="flex items-center gap-1.5 pb-0.5">
|
||||
<iconify-icon icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<header class="relative bg-[#2563EB] text-white flex items-center h-12 px-3 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="hideAddDialog()">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-medium text-white -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="absolute left-1/2 -translate-x-1/2 text-lg font-bold text-white">ULD新增</h1>
|
||||
</header>
|
||||
<div class="flex-1 overflow-y-auto p-4 no-scrollbar">
|
||||
<div class="bg-white rounded-xl shadow-[0_2px_12px_rgba(0,0,0,0.05)] overflow-hidden px-4">
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">ULD编号<span class="text-red-500 ml-0.5">*</span></span>
|
||||
<input id="add-uldNo" class="flex-1 bg-transparent outline-none text-gray-800 placeholder-gray-400 text-[14px]" placeholder="请输入ULD编号" type="text"/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">状态</span>
|
||||
<div class="flex-1 flex items-center justify-between cursor-pointer status-display" onclick="showSelectPanel('status', '请选择状态', ['可用', '占用', '维修'])">
|
||||
<span id="add-status-text" class="text-[14px] text-gray-500">请选择状态</span>
|
||||
<iconify-icon class="text-xl text-gray-500" icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">所属航司</span>
|
||||
<div class="flex-1 flex items-center justify-between cursor-pointer airline-display" onclick="showSelectPanel('airline', '请选择所属航司', ['CA', 'MU', 'CZ', '3U'])">
|
||||
<span id="add-airline-text" class="text-[14px] text-gray-500">请选择所属航司</span>
|
||||
<iconify-icon class="text-xl text-gray-500" icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">来源</span>
|
||||
<div class="flex-1 flex items-center justify-between cursor-pointer source-display" onclick="showSelectPanel('source', '请选择来源', ['空运', '陆运'])">
|
||||
<span id="add-source-text" class="text-[14px] text-gray-500">请选择来源</span>
|
||||
<iconify-icon class="text-xl text-gray-500" icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">自重(kg)<span class="text-red-500 ml-0.5">*</span></span>
|
||||
<input id="add-weight" class="flex-1 bg-transparent outline-none text-gray-800 placeholder-gray-400 text-[14px]" placeholder="请输入自重" type="number"/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">进港航班号</span>
|
||||
<input id="add-flightNo" class="flex-1 bg-transparent outline-none text-gray-800 placeholder-gray-400 text-[14px]" placeholder="请输入进港航班号" type="text"/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">所在港</span>
|
||||
<input id="add-port" class="flex-1 bg-transparent outline-none text-gray-800 placeholder-gray-400 text-[14px]" placeholder="HFE" type="text"/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">入库时间<span class="text-red-500 ml-0.5">*</span></span>
|
||||
<input id="add-inTime" class="flex-1 bg-transparent outline-none text-gray-800 placeholder-gray-400 text-[14px]" placeholder="请输入入库时间" type="text"/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px]">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">备注</span>
|
||||
<input id="add-remark" class="flex-1 bg-transparent outline-none text-gray-800 placeholder-gray-400 text-[14px]" placeholder="请输入备注" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<button class="w-full mt-8 bg-[#2563EB] text-white py-3.5 rounded-[10px] font-bold text-[16px] shadow-lg shadow-blue-500/30 btn-active" onclick="saveUld()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<div class="absolute inset-0 bg-gray-100 z-[70] flex flex-col hidden" id="detailOverlay">
|
||||
<div class="bg-[#2563EB] text-white h-11 px-7 flex items-end pb-2 justify-between text-[14px] font-medium z-50 flex-none">
|
||||
<span>09:41</span>
|
||||
<div class="flex items-center gap-1.5 pb-0.5">
|
||||
<iconify-icon icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<header class="relative bg-[#2563EB] text-white flex items-center h-12 px-3 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="hideUldDetail()">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-medium text-white -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="absolute left-1/2 -translate-x-1/2 text-lg font-bold text-white">ULD详情</h1>
|
||||
<button class="absolute right-4 top-1/2 -translate-y-1/2 text-white text-lg" onclick="openEdit()">
|
||||
<iconify-icon icon="mdi:pencil" width="22"></iconify-icon>
|
||||
</button>
|
||||
</header>
|
||||
<div class="flex-1 overflow-y-auto p-4 no-scrollbar">
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-hidden px-4 mb-6">
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">ULD编号</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-uldNo">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">状态</span>
|
||||
<span class="text-[13px] font-medium px-2 py-0.5 rounded-[6px]" id="detail-status">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">所属航司</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-airline">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">自重 (kg)</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-weight">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">最大装载容积 (m³)</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-volume">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">最大载重 (kg)</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-maxWeight">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">进港航班号</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-flightNo">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">所在港</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-port">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">来源</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-source">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5 border-b border-gray-100">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">入库时间</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-inTime">--</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-3.5">
|
||||
<span class="text-[13px] font-medium text-[#6B7280]">备注</span>
|
||||
<span class="text-[14px] font-normal text-[#111827]" id="detail-remark">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 修改页面 -->
|
||||
<div class="absolute inset-0 bg-gray-100 z-[80] flex flex-col hidden" id="editOverlay">
|
||||
<div class="bg-[#2563EB] text-white h-11 px-7 flex items-end pb-2 justify-between text-[14px] font-medium z-50 flex-none">
|
||||
<span>09:41</span>
|
||||
<div class="flex items-center gap-1.5 pb-0.5">
|
||||
<iconify-icon icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<header class="relative bg-[#2563EB] text-white flex items-center h-12 px-3 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="closeEdit()">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-medium text-white -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="absolute left-1/2 -translate-x-1/2 text-lg font-bold text-white">ULD修改</h1>
|
||||
</header>
|
||||
<div class="flex-1 overflow-y-auto p-4 no-scrollbar">
|
||||
<div class="bg-white rounded-xl shadow-[0_2px_12px_rgba(0,0,0,0.05)] overflow-hidden px-4">
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">ULD编号</span>
|
||||
<input id="edit-uldNo" class="flex-1 bg-transparent outline-none text-gray-800 text-[14px]" type="text" readonly/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">状态</span>
|
||||
<div class="flex-1 flex items-center justify-between cursor-pointer status-display" onclick="showSelectPanel('editStatus', '请选择状态', ['可用', '占用', '维修'])">
|
||||
<span id="edit-status-text" class="text-[13px] font-medium px-2 py-0.5 rounded-[6px] text-gray-800">--</span>
|
||||
<iconify-icon class="text-xl text-gray-500" icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">所属航司</span>
|
||||
<div class="flex-1 flex items-center justify-between cursor-pointer airline-display" onclick="showSelectPanel('editAirline', '请选择航司', ['CA', 'MU', 'CZ', '3U'])">
|
||||
<span id="edit-airline-text" class="text-[14px] text-gray-800">--</span>
|
||||
<iconify-icon class="text-xl text-gray-500" icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">自重(kg)</span>
|
||||
<input id="edit-weight" class="flex-1 bg-transparent outline-none text-gray-800 text-[14px]" type="number"/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">进港航班号</span>
|
||||
<input id="edit-flightNo" class="flex-1 bg-transparent outline-none text-gray-800 text-[14px]" type="text"/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">所在港</span>
|
||||
<input id="edit-port" class="flex-1 bg-transparent outline-none text-gray-800 text-[14px]" type="text"/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">来源</span>
|
||||
<div class="flex-1 flex items-center justify-between cursor-pointer source-display" onclick="showSelectPanel('editSource', '请选择来源', ['空运', '陆运'])">
|
||||
<span id="edit-source-text" class="text-[14px] text-gray-800">--</span>
|
||||
<iconify-icon class="text-xl text-gray-500" icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">入库时间</span>
|
||||
<input id="edit-inTime" class="flex-1 bg-transparent outline-none text-gray-800 text-[14px]" type="text"/>
|
||||
</div>
|
||||
<div class="flex items-center py-[14px]">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">备注</span>
|
||||
<input id="edit-remark" class="flex-1 bg-transparent outline-none text-gray-800 text-[14px]" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<button class="w-full mt-8 bg-[#2563EB] text-white py-3.5 rounded-[10px] font-bold text-[16px] shadow-lg shadow-blue-500/30 btn-active" onclick="saveEdit()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选弹窗 -->
|
||||
<div class="absolute inset-0 z-[100] bg-black/50 flex items-end justify-center" id="filterOverlay" onclick="toggleFilterDrawer(false)" style="display: none;">
|
||||
<div class="bg-white rounded-t-[20px] w-full max-w-[390px] px-5 pt-6 pb-8" onclick="event.stopPropagation()">
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="toggleFilterDrawer(false)">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
<h2 class="text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
</div>
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">入库时间</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 bg-transparent outline-none text-gray-800 placeholder-gray-400 text-[14px]" placeholder="请输入入库时间" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">进港航班号</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入进港航班号" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">所属航司</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择所属航司" readonly type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">来源</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择来源" readonly type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 mt-8">
|
||||
<button class="flex-1 h-11 text-[15px] font-medium text-gray-600 bg-white border border-gray-200 rounded-[10px]" onclick="resetFilter()">重置</button>
|
||||
<button class="flex-1 h-11 text-[15px] font-medium text-white bg-[#2563EB] rounded-[10px] shadow-sm" onclick="confirmFilter()">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 选择面板 -->
|
||||
<div class="fixed inset-0 z-[100] bg-black/40 flex items-end justify-center" id="selectPanel" onclick="hideSelectPanel(event)" style="display: none;">
|
||||
<div class="bg-white rounded-t-[20px] w-full max-w-[390px] px-4 pt-4 pb-8" onclick="event.stopPropagation()">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="w-6"></div>
|
||||
<div class="text-center text-[16px] font-bold text-gray-900" id="selectPanelTitle">请选择</div>
|
||||
<button class="w-6 h-6 flex items-center justify-center text-gray-500" onclick="hideSelectPanel()">
|
||||
<iconify-icon icon="mdi:close" width="20"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-1" id="selectPanelOptions"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentDetail = {};
|
||||
|
||||
function showUldDetail(uldNo, status, airline, weight, volume, maxWeight, flightNo, port, location, remark) {
|
||||
currentDetail = { uldNo, status, airline, weight, volume, maxWeight, flightNo, port, location, remark };
|
||||
document.getElementById('detail-uldNo').innerText = uldNo;
|
||||
document.getElementById('detail-airline').innerText = airline;
|
||||
document.getElementById('detail-weight').innerText = weight;
|
||||
document.getElementById('detail-volume').innerText = volume;
|
||||
document.getElementById('detail-maxWeight').innerText = maxWeight;
|
||||
document.getElementById('detail-flightNo').innerText = flightNo;
|
||||
document.getElementById('detail-port').innerText = port;
|
||||
document.getElementById('detail-remark').innerText = remark;
|
||||
document.getElementById('detail-inTime').innerText = "2026-07-20 15:59:20";
|
||||
document.getElementById('detail-source').innerText = "空运";
|
||||
|
||||
const st = document.getElementById('detail-status');
|
||||
st.innerText = status;
|
||||
st.className = 'text-[11px] px-2 rounded-[6px] font-medium leading-none h-5 flex items-center';
|
||||
if (status === '占用') {
|
||||
st.classList.add('bg-orange-100', 'text-orange-600');
|
||||
} else if (status === '可用') {
|
||||
st.classList.add('bg-green-100','text-green-600');
|
||||
} else if (status === '维修') {
|
||||
st.classList.add('bg-red-100','text-red-600');
|
||||
} else {
|
||||
st.className = 'text-[14px] font-normal text-[#111827]';
|
||||
}
|
||||
|
||||
document.getElementById('detailOverlay').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function hideUldDetail() {
|
||||
document.getElementById('detailOverlay').classList.add('hidden');
|
||||
}
|
||||
|
||||
function openEdit() {
|
||||
document.getElementById('editOverlay').classList.remove('hidden');
|
||||
document.getElementById('edit-uldNo').value = currentDetail.uldNo;
|
||||
document.getElementById('edit-weight').value = currentDetail.weight;
|
||||
document.getElementById('edit-flightNo').value = currentDetail.flightNo;
|
||||
document.getElementById('edit-port').value = currentDetail.port;
|
||||
document.getElementById('edit-inTime').value = "2026-07-20 15:59:20";
|
||||
document.getElementById('edit-remark').value = currentDetail.remark;
|
||||
|
||||
const st = document.getElementById('edit-status-text');
|
||||
st.textContent = currentDetail.status;
|
||||
st.className = 'text-[13px] font-medium px-2 py-0.5 rounded-[6px]';
|
||||
if (currentDetail.status === '占用') {
|
||||
st.classList.add('bg-[#FFF3E0]','text-[#E67722]');
|
||||
} else if (currentDetail.status === '可用') {
|
||||
st.classList.add('bg-green-100','text-green-600');
|
||||
} else if (currentDetail.status === '维修') {
|
||||
st.classList.add('bg-red-100','text-red-600');
|
||||
} else {
|
||||
st.classList.add('text-gray-800');
|
||||
}
|
||||
|
||||
document.getElementById('edit-airline-text').textContent = currentDetail.airline;
|
||||
document.getElementById('edit-source-text').textContent = '空运';
|
||||
}
|
||||
|
||||
function closeEdit() {
|
||||
document.getElementById('editOverlay').classList.add('hidden');
|
||||
}
|
||||
|
||||
function saveEdit() {
|
||||
alert('修改成功');
|
||||
closeEdit();
|
||||
hideUldDetail();
|
||||
}
|
||||
|
||||
function toggleFilterDrawer(show) {
|
||||
document.getElementById('filterOverlay').style.display = show ? 'flex' : 'none';
|
||||
}
|
||||
function resetFilter() {
|
||||
document.querySelectorAll('#filterOverlay input').forEach(i=>i.value='');
|
||||
}
|
||||
function confirmFilter() {
|
||||
toggleFilterDrawer(false);
|
||||
}
|
||||
|
||||
function toggleSelectAll(checked) {
|
||||
document.querySelectorAll('main .custom-checkbox-round').forEach(cb=>cb.checked=checked);
|
||||
updateSelectedCount();
|
||||
}
|
||||
function handleCheckboxClick() {
|
||||
updateSelectedCount();
|
||||
}
|
||||
function updateSelectedCount() {
|
||||
const total = document.querySelectorAll('main .custom-checkbox-round').length;
|
||||
const ckd = document.querySelectorAll('main .custom-checkbox-round:checked').length;
|
||||
document.getElementById('selected-count').innerText = ckd+' / '+total;
|
||||
document.getElementById('select-all-cb').checked = total > 0 && total === ckd;
|
||||
}
|
||||
|
||||
function showAddDialog() {
|
||||
document.getElementById('addUldOverlay').classList.remove('hidden');
|
||||
}
|
||||
function hideAddDialog() {
|
||||
document.getElementById('addUldOverlay').classList.add('hidden');
|
||||
}
|
||||
function saveUld() {
|
||||
alert('保存成功');
|
||||
hideAddDialog();
|
||||
}
|
||||
|
||||
let currentSelectField = '';
|
||||
function showSelectPanel(field, title, options) {
|
||||
currentSelectField = field;
|
||||
document.getElementById('selectPanelTitle').textContent = title;
|
||||
const box = document.getElementById('selectPanelOptions');
|
||||
box.innerHTML = '';
|
||||
options.forEach(opt=>{
|
||||
const d = document.createElement('div');
|
||||
d.className = 'py-3 px-4 text-center border-b cursor-pointer hover:bg-gray-50';
|
||||
if(field.includes('status')){
|
||||
let cls = opt=='占用'?'bg-[#FFF3E0] text-[#E67722]':
|
||||
opt=='可用'?'bg-green-100 text-green-600':'bg-red-100 text-red-600';
|
||||
d.innerHTML = `<span class="${cls} px-2 py-0.5 rounded-[6px] text-sm font-medium">${opt}</span>`;
|
||||
} else {
|
||||
d.textContent = opt;
|
||||
}
|
||||
d.onclick = ()=>{
|
||||
if(field === 'editStatus'){
|
||||
const t = document.getElementById('edit-status-text');
|
||||
t.textContent = opt;
|
||||
t.className = 'text-[13px] font-medium px-2 py-0.5 rounded-[6px]';
|
||||
if(opt=='占用'){
|
||||
t.classList.add('bg-[#FFF3E0]','text-[#E67722]');
|
||||
}
|
||||
if(opt=='可用')t.classList.add('bg-green-100','text-green-600');
|
||||
if(opt=='维修')t.classList.add('bg-red-100','text-red-600');
|
||||
}
|
||||
if(field === 'editAirline') document.getElementById('edit-airline-text').textContent = opt;
|
||||
if(field === 'editSource') document.getElementById('edit-source-text').textContent = opt;
|
||||
if(field === 'status') document.getElementById('add-status-text').textContent = opt;
|
||||
if(field === 'airline') document.getElementById('add-airline-text').textContent = opt;
|
||||
if(field === 'source') document.getElementById('add-source-text').textContent = opt;
|
||||
hideSelectPanel();
|
||||
};
|
||||
box.appendChild(d);
|
||||
});
|
||||
document.getElementById('selectPanel').style.display = 'flex';
|
||||
}
|
||||
function hideSelectPanel() {
|
||||
document.getElementById('selectPanel').style.display = 'none';
|
||||
}
|
||||
|
||||
function handleBatchDelete() {
|
||||
const ckd = document.querySelectorAll('main .custom-checkbox-round:checked').length;
|
||||
if(ckd===0)return alert('请选择');
|
||||
if(confirm('确定删除?')) alert('删除成功');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
715
documents/5.5寸手持端设计文件/02_出库交接/出库交接.html
Normal file
715
documents/5.5寸手持端设计文件/02_出库交接/出库交接.html
Normal file
@@ -0,0 +1,715 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>出库交接 - 移动端原型</title>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<!-- Iconify -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'PingFang SC';
|
||||
src: local('PingFang SC');
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #e2e8f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
/* 模拟手机容器 */
|
||||
.phone-container {
|
||||
width: 375px;
|
||||
height: 812px;
|
||||
background-color: #f5f6fa;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
border: 8px solid #1a1a1a;
|
||||
border-radius: 40px;
|
||||
}
|
||||
/* 隐藏滚动条但保留功能 */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
/* 页面切换动画 */
|
||||
.page {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
.page.active {
|
||||
display: flex;
|
||||
}
|
||||
/* 按钮点击态 */
|
||||
.btn-active:active {
|
||||
opacity: 0.7;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
/* 复选框样式 */
|
||||
.custom-checkbox {
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #cbd5e1;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.custom-checkbox:checked {
|
||||
background-color: #2563EB;
|
||||
border-color: #2563EB;
|
||||
}
|
||||
.custom-checkbox:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.custom-checkbox-round {
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #cbd5e1;
|
||||
border-radius: 50%;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.custom-checkbox-round:checked {
|
||||
background-color: #2563EB;
|
||||
border-color: #2563EB;
|
||||
}
|
||||
.custom-checkbox-round:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
/* 卡片选中样式已移除 */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="phone-container">
|
||||
<!-- 状态栏模拟 -->
|
||||
<div class="h-10 w-full flex justify-between items-center px-8 flex-none bg-[#2563EB] text-white">
|
||||
<span class="text-xs font-semibold">09:41</span>
|
||||
<div class="flex space-x-1 items-center">
|
||||
<iconify-icon icon="mdi:signal" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面1: 货运列表首页 -->
|
||||
<div class="page active" id="page-home">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="bg-[#2563EB] text-white h-12 flex items-center justify-between px-4 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="alert('返回首页')">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-bold -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium">出库交接</h1>
|
||||
<div class="w-16"></div>
|
||||
</div>
|
||||
|
||||
<!-- 内容滚动区 -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar pb-24">
|
||||
<!-- 搜索栏 -->
|
||||
<section class="px-4 pt-4 pb-3 bg-white">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center bg-white border border-gray-200 shadow-sm rounded-[12px] h-10 px-3 gap-2 flex-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px] text-lg flex-none" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none rounded-lg px-2" placeholder="搜索ULD编号" type="text"/>
|
||||
<button class="flex-none text-blue-500">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="toggleFilterDrawer(true)">
|
||||
<iconify-icon class="text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 子Tab切换栏 -->
|
||||
<div class="flex bg-white border-b sticky top-0 z-20">
|
||||
<button class="flex-1 py-3 text-sm font-bold text-[#1F2937] border-b-2 border-[#1F2937] transition-all" id="tab-pending" onclick="switchTab('pending')">
|
||||
待交接 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5">3</span>
|
||||
</button>
|
||||
<button class="flex-1 py-3 text-sm font-bold text-gray-500 text-[14px] border-b-2 border-transparent transition-all" id="tab-shipped" onclick="switchTab('shipped')">
|
||||
已交接 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5">3</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<div class="p-3 pt-4 space-y-3" id="list-pending">
|
||||
<!-- 卡片 1 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">PMC90901CZ</div>
|
||||
</div>
|
||||
<div class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">IMP代码</div>
|
||||
</div>
|
||||
<!-- 航班信息行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260706/MU1111</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">架子车号</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">076</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">目的港</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">LAX</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">装机重</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">200kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 库位信息行 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">库位:大棚A3</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: 'PMC87678MU', status: '待交接', rackNo: '076', location: 'A区-03货位', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片 2 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">AKE87865MU</div>
|
||||
</div>
|
||||
<div class="bg-red-600 text-white text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">IMP代码</div>
|
||||
</div>
|
||||
<!-- 航班信息行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260706/CA2222</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">架子车号</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">066</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">目的港</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">LAX</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">装机重</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">150kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 库位信息行 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">库位:空侧-05</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: 'AKE87865MU', status: '待交接', agent: '043', flightDate: '20260706', flightNo: 'CA2222', pieces: '85', weight: '150', rackNo: '055', carrier: 'CA', origin: 'PEK', location: 'B区-05货位', code: '076', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片 3 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">PMC87678MU</div>
|
||||
</div>
|
||||
<div class="bg-red-600 text-white text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">IMP代码</div>
|
||||
</div>
|
||||
<!-- 航班信息行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260707/3U3333</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">架子车号</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">055</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">目的港</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">LAX</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">装机重</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">450kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 库位信息行 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">库位:C区-01货位</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: 'PMC87678MU', status: '待交接', agent: 'DHL', flightDate: '20260707', flightNo: '3U3333', pieces: '200', weight: '450', rackNo: '055', carrier: '3U', origin: 'CTU', location: 'C区-01货位', code: '076', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已交接列表内容 -->
|
||||
<div class="p-3 pt-4 space-y-3 hidden" id="list-shipped">
|
||||
<!-- 卡片 1 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">PMC90901CZ</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已交接</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作信息行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260704/MU1111</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">架子车号</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">076</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">目的港</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">LAX</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">装机重</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">80kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作人信息 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">交接人:张三 · 交接时间:2026-07-05 11:45</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer" onclick="showDetail({id: 'PMC90901CZ', status: '已交接', agent: '076', rackNo: '076', location: 'B区-05货位', code: '076', counterTime: '2026-07-05 10:30', counterPerson: '张三', outboundTime: '2026-07-05 11:45', outboundPerson: '张三'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片 2 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">AKE87865MU</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已交接</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作信息行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260705/CA2222</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">架子车号</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">043</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">目的港</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">LAX</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">装机重</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">300kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作人信息 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">交接人:赵六 · 交接时间:2026-07-06 10:30</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer" onclick="showDetail({id: 'AKE87865MU', status: '已交接', agent: '043', flightDate: '20260705', flightNo: 'CA2222', pieces: '120', weight: '300', pickupNo: 'TH20260702002', rackNo: '鲜花绿植', carrier: 'CA', origin: 'SFO', location: 'A区-08货位', code: '076', counterTime: '2026-07-06 09:15', counterPerson: '王五', outboundTime: '2026-07-06 10:30', outboundPerson: '赵六'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片 3 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">PMC87678MU</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已交接</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作信息行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260705/3U3333</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">架子车号</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">077</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">目的港</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">LAX</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">装机重</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">160kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作人信息 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">交接人:孙八 · 交接时间:2026-07-06 15:20</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer" onclick="showDetail({id: 'PMC87678MU', status: '已交接', agent: 'DHL', flightDate: '20260705', flightNo: '3U3333', pieces: '75', weight: '160', pickupNo: 'TH20260703003', rackNo: '生鲜食品', carrier: '3U', origin: 'CDG', location: 'D区-02货位', code: 'NOR', counterTime: '2026-07-06 14:00', counterPerson: '钱七', outboundTime: '2026-07-06 15:20', outboundPerson: '孙八'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部工具栏 -->
|
||||
<div class="absolute bottom-0 w-full bg-white border-t border-gray-200 z-40 flex flex-col pt-4 pb-4" id="bottom-toolbar">
|
||||
<!-- 操作区域 -->
|
||||
<div class="px-4 flex items-center w-full">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="custom-checkbox-round" id="select-all-cb" onclick="toggleSelectAll(this.checked)" type="checkbox"/>
|
||||
<label class="text-[13px] text-[#1F2937] font-medium cursor-pointer" for="select-all-cb">全选</label>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-[12px] text-[#9CA3AF]">已选 <span id="selected-count">0</span> 项</span>
|
||||
<button class="bg-[#2563EB] text-white text-[15px] font-bold px-5 py-2 rounded-[10px] shadow-lg shadow-blue-500/25 btn-active" onclick="alert('交接操作')">
|
||||
交接
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面2: 详情页 -->
|
||||
<div class="page" id="page-detail">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="bg-[#2563EB] text-white h-12 flex items-center justify-between px-4 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="switchPage('home')">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-bold -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium">板箱详情</h1>
|
||||
<div class="w-16"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar pb-8 p-4">
|
||||
<!-- 货运单据样式卡片 -->
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-hidden border border-gray-100">
|
||||
<!-- 1. 顶部蓝色条带 -->
|
||||
<div class="bg-[#2563EB] px-4 py-3 flex justify-between items-center text-white">
|
||||
<span class="font-bold text-[17px] leading-tight" id="detail-awb">781-22222620</span>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<!-- 3. 货物名称 -->
|
||||
<div class="text-[22px] font-bold text-[#111827] mb-3" id="detail-rack-no">076</div>
|
||||
<!-- 4. 两列信息表格 -->
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-3">
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">装机重</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-count">55</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">航班日期</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-weight">20260706</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">航班号</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-carrier">MU2231</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">目的港</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-flight-date">LAX</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">IMP代码</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-carrier">X</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">板型</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-flight-date">QP</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">库位</div>
|
||||
<div class="flex">
|
||||
<span class="bg-orange-50 text-orange-600 px-1 rounded text-[15px] font-bold" id="detail-location">1111111</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">备注</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-flight-no">危险品</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 复磅信息卡片 -->
|
||||
<div class="mt-4 bg-white rounded-xl shadow-sm overflow-hidden border border-gray-200 hidden" id="detail-outbound-card">
|
||||
<div class="bg-[#10B981] px-4 py-2.5">
|
||||
<span class="text-white font-semibold text-[15px]">复磅信息</span>
|
||||
</div>
|
||||
<div class="bg-white px-4 py-3 grid grid-cols-2 gap-y-3">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">复磅人</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-counter-person">-</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">复磅时间</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-counter-time">-</span>
|
||||
</div>
|
||||
<div class="flex flex-col" id="detail-outbound-person-row">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">交接人</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-outbound-person">-</span>
|
||||
</div>
|
||||
<div class="flex flex-col" id="detail-outbound-time-row">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">交接时间</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-outbound-time">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选条件弹窗(从ULD管理移植) -->
|
||||
<div class="absolute inset-0 z-[100] bg-black/50 flex items-end justify-center" id="filterOverlay" onclick="toggleFilterDrawer(false)" style="display: none;">
|
||||
<div class="bg-white rounded-t-[20px] w-full max-w-[375px] px-5 pt-6 pb-8" onclick="event.stopPropagation()">
|
||||
<!-- 标题 -->
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="toggleFilterDrawer(false)">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
<h2 class="text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
</div>
|
||||
|
||||
<div class="space-y-5">
|
||||
<!-- 航班日期 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班日期</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择航班日期" readonly type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 进港航班号 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班号</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入航班号" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 交接人 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">交接人</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择交接人" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部按钮 -->
|
||||
<div class="flex gap-3 mt-8">
|
||||
<button class="flex-1 h-11 text-[15px] font-medium text-gray-600 bg-white border border-gray-200 rounded-[10px]" onclick="resetFilter()">重置</button>
|
||||
<button class="flex-1 h-11 text-[15px] font-medium text-white bg-[#2563EB] rounded-[10px] shadow-sm" onclick="confirmFilter()">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 页面切换逻辑
|
||||
function switchPage(pageId) {
|
||||
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
||||
document.getElementById('page-' + pageId).classList.add('active');
|
||||
if(pageId === 'detail') {
|
||||
document.querySelector('#page-detail .overflow-y-auto').scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 子Tab切换逻辑
|
||||
function switchTab(type) {
|
||||
const tabPending = document.getElementById('tab-pending');
|
||||
const tabShipped = document.getElementById('tab-shipped');
|
||||
const listPending = document.getElementById('list-pending');
|
||||
const listShipped = document.getElementById('list-shipped');
|
||||
const bottomToolbar = document.getElementById('bottom-toolbar');
|
||||
|
||||
if (type === 'pending') {
|
||||
// 待交接
|
||||
tabPending.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabPending.classList.remove('text-gray-500', 'border-transparent');
|
||||
|
||||
tabShipped.classList.add('text-gray-500', 'border-transparent');
|
||||
tabShipped.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
|
||||
listPending.classList.remove('hidden');
|
||||
listShipped.classList.add('hidden');
|
||||
bottomToolbar.style.display = 'flex';
|
||||
} else {
|
||||
// 已交接
|
||||
tabShipped.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabShipped.classList.remove('text-gray-500', 'border-transparent');
|
||||
|
||||
tabPending.classList.add('text-gray-500', 'border-transparent');
|
||||
tabPending.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
|
||||
listShipped.classList.remove('hidden');
|
||||
listPending.classList.add('hidden');
|
||||
bottomToolbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 全选/取消全选
|
||||
function toggleSelectAll(checked) {
|
||||
const checkboxes = document.querySelectorAll('.item-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = checked;
|
||||
});
|
||||
updateSelectionCount();
|
||||
}
|
||||
|
||||
// 处理卡片复选框点击
|
||||
function handleCheckboxClick(checkbox, event) {
|
||||
event.stopPropagation();
|
||||
updateSelectionCount();
|
||||
const checkboxes = document.querySelectorAll('.item-checkbox');
|
||||
const selectAllCb = document.getElementById('select-all-cb');
|
||||
const allChecked = Array.from(checkboxes).every(cb => cb.checked);
|
||||
selectAllCb.checked = allChecked;
|
||||
}
|
||||
|
||||
// 更新选中计数
|
||||
function updateSelectionCount() {
|
||||
const checkedCount = document.querySelectorAll('.item-checkbox:checked').length;
|
||||
document.getElementById('selected-count').textContent = checkedCount;
|
||||
}
|
||||
|
||||
// 显示详情页并填充数据
|
||||
function showDetail(data) {
|
||||
if (!data) return;
|
||||
const setText = (id, text) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.textContent = text || '-';
|
||||
};
|
||||
|
||||
setText('detail-awb', data.id);
|
||||
setText('detail-rack-no', data.rackNo);
|
||||
setText('detail-weight', (data.weight || '') + (data.weight && !data.weight.toString().includes('kg') ? ' kg' : ''));
|
||||
setText('detail-flight-date', data.flightDate);
|
||||
setText('detail-flight-no', data.flightNo);
|
||||
setText('detail-location', data.location);
|
||||
|
||||
const outboundCard = document.getElementById('detail-outbound-card');
|
||||
if (outboundCard) {
|
||||
outboundCard.classList.remove('hidden');
|
||||
setText('detail-counter-person', data.counterPerson);
|
||||
setText('detail-counter-time', data.counterTime);
|
||||
setText('detail-outbound-person', data.outboundPerson);
|
||||
setText('detail-outbound-time', data.outboundTime);
|
||||
|
||||
const outboundPersonRow = document.getElementById('detail-outbound-person-row');
|
||||
const outboundTimeRow = document.getElementById('detail-outbound-time-row');
|
||||
|
||||
if (data.status === '已交接') {
|
||||
outboundPersonRow.classList.remove('hidden');
|
||||
outboundTimeRow.classList.remove('hidden');
|
||||
} else {
|
||||
outboundPersonRow.classList.add('hidden');
|
||||
outboundTimeRow.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
switchPage('detail');
|
||||
}
|
||||
|
||||
// 筛选面板(移植自ULD管理)
|
||||
function toggleFilterDrawer(show) {
|
||||
const overlay = document.getElementById('filterOverlay');
|
||||
overlay.style.display = show ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
function resetFilter() {
|
||||
const inputs = document.querySelectorAll('#filterOverlay input');
|
||||
inputs.forEach(input => input.value = '');
|
||||
}
|
||||
|
||||
function confirmFilter() {
|
||||
toggleFilterDrawer(false);
|
||||
}
|
||||
|
||||
// 初始化日期
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
console.log("System date initialized:", today);
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
151
documents/5.5寸手持端设计文件/03_出港计重/detail.html
Normal file
151
documents/5.5寸手持端设计文件/03_出港计重/detail.html
Normal file
@@ -0,0 +1,151 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>运单详情</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #F3F4F6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "PingFang SC", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
/* 统一手机外壳容器 */
|
||||
.phone-container {
|
||||
width: 390px;
|
||||
height: 844px;
|
||||
background: #1a1a1a;
|
||||
border-radius: 55px;
|
||||
border: 8px solid #1F2937;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 30px 60px rgba(0,0,0,0.3);
|
||||
position: relative;
|
||||
}
|
||||
/* 手机内部屏幕区域 */
|
||||
.phone-screen {
|
||||
flex: 1;
|
||||
background: #F2F5F9;
|
||||
margin: 0 4px 4px 4px;
|
||||
border-radius: 46px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.content-scroll {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.content-scroll::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="phone-container">
|
||||
<div class="phone-screen">
|
||||
|
||||
<!-- 状态栏 + 导航栏 蓝色连体区域 -->
|
||||
<div class="bg-[#2563EB] flex flex-col flex-none">
|
||||
<!-- 手机状态栏 -->
|
||||
<div class="flex items-center justify-between h-[28px] text-white text-[12px] px-6">
|
||||
<span class="text-white font-medium tracking-wide">09:10</span>
|
||||
<div class="flex items-center gap-[5px]">
|
||||
<iconify-icon class="text-white text-[13px]" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-white text-[13px]" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-white text-[15px]" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<div class="text-white h-12 flex items-center px-4">
|
||||
<a class="flex items-center gap-1" href="records.html">
|
||||
<iconify-icon class="text-xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span class="text-[16px]">返回</span>
|
||||
</a>
|
||||
<h1 class="flex-1 text-center text-[17px] font-semibold mr-10">运单详情</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-scroll">
|
||||
<!-- 卡片1 — 运单概览 -->
|
||||
<div class="bg-white rounded-xl shadow-md mb-4 overflow-hidden border border-gray-100 mx-4 mt-4">
|
||||
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800" id="det-uldTitle">基本信息</span>
|
||||
</div>
|
||||
|
||||
<div class="p-3 grid grid-cols-2 gap-y-3 gap-x-4 border-t border-gray-50 pt-3">
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">运单号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-billNo">78111112222</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">件数</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-piece">85</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">重量</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-weight">156 kg</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">特码</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-code">DGR</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">代理</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-agent">ATR</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">航班日期</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-flightDate">2026-07-03</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">航班号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-flightNo">MU2222</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">航程</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-route">PVG - CAN</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">业务类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-type">普通货物运输</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">通道号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-channel">出港收运1</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">品名</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-itemName">电子产品</span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 mb-0.5">备注</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-remark">测试</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pb-6"></div>
|
||||
</div>
|
||||
|
||||
<!-- iOS 底部指示条 -->
|
||||
<div class="h-5 bg-[#F2F5F9] flex justify-center items-center flex-none">
|
||||
<div class="w-[134px] h-[5px] bg-gray-300 rounded-full"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
419
documents/5.5寸手持端设计文件/03_出港计重/main.html
Normal file
419
documents/5.5寸手持端设计文件/03_出港计重/main.html
Normal file
@@ -0,0 +1,419 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>出港待计重</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #F3F4F6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "PingFang SC", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
/* 统一手机外壳容器 */
|
||||
.phone-container {
|
||||
width: 390px;
|
||||
height: 844px;
|
||||
background: #1a1a1a;
|
||||
border-radius: 55px;
|
||||
border: 8px solid #1F2937;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 30px 60px rgba(0,0,0,0.3);
|
||||
position: relative;
|
||||
}
|
||||
/* 手机内部屏幕区域 */
|
||||
.phone-screen {
|
||||
flex: 1;
|
||||
background: #F2F5F9;
|
||||
margin: 0 4px 4px 4px;
|
||||
border-radius: 46px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
/* 隐藏滚动条 */
|
||||
.no-scrollbar::-webkit-scrollbar { display: none; }
|
||||
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
/* 卡片淡入动画 */
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(18px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
.card-animate {
|
||||
animation: fadeInUp 0.5s ease-out both;
|
||||
}
|
||||
.card-animate:nth-child(1) { animation-delay: 0.05s; }
|
||||
.card-animate:nth-child(2) { animation-delay: 0.12s; }
|
||||
.card-animate:nth-child(3) { animation-delay: 0.19s; }
|
||||
.card-animate:nth-child(4) { animation-delay: 0.26s; }
|
||||
/* 卡片点击涟漪效果 */
|
||||
.card-item {
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
.card-item:active {
|
||||
transform: scale(0.985);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
||||
}
|
||||
/* 筛选弹窗动画 */
|
||||
.filter-modal {
|
||||
transition: transform 0.3s ease-out;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
.filter-modal.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.filter-overlay {
|
||||
transition: opacity 0.3s ease;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.filter-overlay.show {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="phone-container">
|
||||
<div class="phone-screen">
|
||||
<!-- ===== 顶部连体蓝色区域【状态栏+导航栏】 ===== -->
|
||||
<div class="bg-[#2563EB] flex flex-col flex-none">
|
||||
<!-- 手机状态栏 -->
|
||||
<div class="flex items-center justify-between h-[28px] text-white text-[12px] px-6">
|
||||
<span class="text-white font-medium tracking-wide">09:10</span>
|
||||
<div class="flex items-center gap-[5px]">
|
||||
<iconify-icon class="text-white text-[13px]" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-white text-[13px]" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-white text-[15px]" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<header class="text-white h-12 flex items-center px-4">
|
||||
<button class="flex items-center text-[15px] gap-0.5" onclick="window.history.back()">
|
||||
<iconify-icon class="text-xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="flex-1 text-center text-[17px] font-semibold">出港待计重</h1>
|
||||
<div class="w-12"></div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<!-- ===== 主内容区 ===== -->
|
||||
<main class="flex-1 overflow-y-auto no-scrollbar bg-[#F2F5F9]">
|
||||
<!-- 搜索栏 -->
|
||||
<div class="h-[3px] bg-blue-500 mx-0"></div>
|
||||
<section class="px-4 pt-4 pb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center bg-white border border-gray-200 shadow-sm rounded-[12px] h-10 px-3 gap-2 flex-1">
|
||||
<iconify-icon class="text-gray-400 text-lg flex-none" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none rounded-lg px-2" placeholder="搜索运单号/航班号..." type="text"/>
|
||||
<button class="flex-none text-blue-500">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="toggleFilterDrawer(true)">
|
||||
<iconify-icon class="text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 统计标签 -->
|
||||
<div class="flex items-center gap-2 px-4 pb-3">
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#2563EB]">
|
||||
<div class="text-[11px] text-gray-500">总票数</div>
|
||||
<div class="text-[22px] font-bold text-[#2563EB] leading-tight mt-0.5">4</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-orange-500">
|
||||
<div class="text-[11px] text-gray-500">待计重</div>
|
||||
<div class="text-[22px] font-bold text-orange-500 leading-tight mt-0.5">2</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#10B981]">
|
||||
<div class="text-[11px] text-gray-500">计重中</div>
|
||||
<div class="text-[22px] font-bold text-[#10B981] leading-tight mt-0.5">2</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== 运单卡片列表 ===== -->
|
||||
<div class="px-0 pt-3 pb-8">
|
||||
<!-- 卡片 1 -->
|
||||
<div class="card-item card-animate bg-white rounded-2xl shadow-sm shadow-black/5 px-4 py-3.5 mx-4 mb-2.5 cursor-pointer" onclick="window.location.href='weighing.html'">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<div class="flex items-center">
|
||||
<span class="text-[15px] font-bold text-[#1F2937]">78133363398</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">未计重</span>
|
||||
<iconify-icon class="text-gray-400" icon="mdi:chevron-right" width="14"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span>日期:20260721</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>航程:HFE-LAX</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:truck-outline"></iconify-icon>
|
||||
<span>代理:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:DGR</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="w-full bg-[#EFF6FF] text-[#2563EB] text-xs py-2 rounded-[6px] font-medium h-8 flex items-center justify-center" onclick="event.stopPropagation(); this.disabled=true; this.classList.remove('bg-[#EFF6FF]','text-[#2563EB]'); this.classList.add('bg-gray-100','text-gray-400','cursor-not-allowed'); showToast('执行提前运抵:运单号 78133363396')">
|
||||
提前运抵
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片 2 -->
|
||||
<div class="card-item card-animate bg-white rounded-2xl shadow-sm shadow-black/5 px-4 py-3.5 mx-4 mb-2.5 cursor-pointer" onclick="window.location.href='weighing.html'">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<div class="flex items-center">
|
||||
<span class="text-[15px] font-bold text-[#1F2937]">78133363399</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">计重中</span>
|
||||
<iconify-icon class="text-gray-400" icon="mdi:chevron-right" width="14"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span>日期:20260721</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>航程:HFE-LAX</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:truck-outline"></iconify-icon>
|
||||
<span>代理:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:DGR</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="w-full bg-[#EFF6FF] text-[#2563EB] text-xs py-2 rounded-[6px] font-medium h-8 flex items-center justify-center" onclick="event.stopPropagation(); this.disabled=true; this.classList.remove('bg-[#EFF6FF]','text-[#2563EB]'); this.classList.add('bg-gray-100','text-gray-400','cursor-not-allowed'); showToast('执行提前运抵:运单号 78133363397')">
|
||||
提前运抵
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片 3 -->
|
||||
<div class="card-item card-animate bg-white rounded-2xl shadow-sm shadow-black/5 px-4 py-3.5 mx-4 mb-2.5 cursor-pointer" onclick="window.location.href='weighing.html'">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<div class="flex items-center">
|
||||
<span class="text-[15px] font-bold text-[#1F2937]">78133363398</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">未计重</span>
|
||||
<iconify-icon class="text-gray-400" icon="mdi:chevron-right" width="14"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span>日期:20260721</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>航程:HFE-LAX</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:truck-outline"></iconify-icon>
|
||||
<span>代理:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:DGR</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="w-full bg-[#EFF6FF] text-[#2563EB] text-xs py-2 rounded-[6px] font-medium h-8 flex items-center justify-center" onclick="event.stopPropagation(); this.disabled=true; this.classList.remove('bg-[#EFF6FF]','text-[#2563EB]'); this.classList.add('bg-gray-100','text-gray-400','cursor-not-allowed'); showToast('执行提前运抵:运单号 78133363398')">
|
||||
提前运抵
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片 4 -->
|
||||
<div class="card-item card-animate bg-white rounded-2xl shadow-sm shadow-black/5 px-4 py-3.5 mx-4 mb-2.5 cursor-pointer" onclick="window.location.href='weighing.html'">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<div class="flex items-center">
|
||||
<span class="text-[15px] font-bold text-[#1F2937]">78133363399</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">计重中</span>
|
||||
<iconify-icon class="text-gray-400 text-lg shrink-0" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span>日期:20260721</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>航程:HFE-LAX</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:truck-outline"></iconify-icon>
|
||||
<span>代理:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:DGR</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="w-full bg-[#EFF6FF] text-[#2563EB] text-xs py-2 rounded-[6px] font-medium h-8 flex items-center justify-center" onclick="event.stopPropagation(); this.disabled=true; this.classList.remove('bg-[#EFF6FF]','text-[#2563EB]'); this.classList.add('bg-gray-100','text-gray-400','cursor-not-allowed'); showToast('执行提前运抵:运单号 78133363399')">
|
||||
提前运抵
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- ===== iOS 底部指示条 ===== -->
|
||||
<div class="h-5 bg-[#F2F5F9] flex justify-center items-center flex-none">
|
||||
<div class="w-[134px] h-[5px] bg-gray-300 rounded-full"></div>
|
||||
</div>
|
||||
|
||||
<!-- 计重记录悬浮按钮 -->
|
||||
<a class="absolute bottom-[20px] right-[20px] w-[52px] h-[52px] bg-[#2563EB] rounded-full shadow-lg shadow-blue-500/30 flex items-center justify-center z-50 hover:bg-[#1d4ed8] active:scale-95 transition-transform" href="records.html">
|
||||
<iconify-icon class="text-white text-[24px]" icon="mdi:clipboard-text-clock"></iconify-icon>
|
||||
</a>
|
||||
|
||||
<!-- ================== 筛选弹窗(底部弹出) ================== -->
|
||||
<div class="filter-overlay absolute inset-0 bg-black/40 z-40" id="filterOverlay" onclick="toggleFilterDrawer(false)"></div>
|
||||
<div class="filter-modal absolute bottom-0 left-0 right-0 bg-white rounded-t-3xl z-50 p-6 shadow-2xl" id="filter-panel">
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<h2 class="text-center text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="toggleFilterDrawer(false)">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">通道号</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择通道号</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4 mt-4">
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">承运人</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-800">
|
||||
<span class="text-sm font-medium">MU</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">运单类型</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择运单类型</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">代理人</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择代理人</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 pt-2">
|
||||
<button class="flex-1 bg-white border border-gray-300 text-gray-600 py-3.5 rounded-xl font-bold" onclick="resetFilter()">
|
||||
重置
|
||||
</button>
|
||||
<button class="flex-1 bg-[#2563EB] text-white py-3.5 rounded-xl font-bold shadow-lg shadow-blue-500/20" onclick="confirmFilter()">
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-8"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Toast 提示函数
|
||||
function showToast(msg) {
|
||||
var existing = document.querySelector('.custom-toast');
|
||||
if (existing) existing.remove();
|
||||
var toast = document.createElement('div');
|
||||
toast.className = 'custom-toast fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-gray-900 text-white text-sm px-5 py-3 rounded-xl shadow-lg z-50 transition-opacity duration-200';
|
||||
toast.style.maxWidth = '280px';
|
||||
toast.style.textAlign = 'center';
|
||||
toast.style.pointerEvents = 'none';
|
||||
toast.textContent = msg;
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(function() { toast.style.opacity = '0'; setTimeout(function() { toast.remove(); }, 200); }, 1500);
|
||||
}
|
||||
|
||||
// 筛选弹窗显示/隐藏
|
||||
function toggleFilterDrawer(show) {
|
||||
const overlay = document.getElementById('filterOverlay');
|
||||
const modal = document.getElementById('filter-panel');
|
||||
if (show) {
|
||||
overlay.classList.add('show');
|
||||
modal.classList.add('show');
|
||||
} else {
|
||||
overlay.classList.remove('show');
|
||||
modal.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
// 重置筛选
|
||||
function resetFilter() {
|
||||
showToast('已重置筛选条件');
|
||||
}
|
||||
|
||||
// 确认筛选
|
||||
function confirmFilter() {
|
||||
toggleFilterDrawer(false);
|
||||
showToast('已应用筛选条件');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
282
documents/5.5寸手持端设计文件/03_出港计重/records.html
Normal file
282
documents/5.5寸手持端设计文件/03_出港计重/records.html
Normal file
@@ -0,0 +1,282 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>计重记录 - 国际货运管理系统</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.hide-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.filter-modal {
|
||||
transition: transform 0.3s ease-out;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
.filter-modal.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.filter-overlay {
|
||||
transition: opacity 0.3s ease;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.filter-overlay.show {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.btn-active:active {
|
||||
opacity: 0.7;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-200 min-h-screen flex justify-center items-center p-4">
|
||||
<!-- 手机容器 -->
|
||||
<div class="w-[375px] h-[812px] bg-[#F3F4F6] overflow-hidden rounded-[40px] border-[8px] border-gray-800 shadow-2xl relative flex flex-col">
|
||||
<!-- iOS 状态栏 -->
|
||||
<div class="flex-none bg-[#2563EB] text-white h-11 px-6 flex justify-between items-center z-50">
|
||||
<span class="text-sm font-semibold">09:10</span>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-lg" icon="mdi:signal"></iconify-icon>
|
||||
<iconify-icon class="text-lg" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-lg rotate-90" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<div class="flex-none bg-[#2563EB] h-12 flex items-center px-4 relative">
|
||||
<button class="flex items-center text-white text-base btn-active" onclick="window.location.href='main.html'">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="absolute left-1/2 -translate-x-1/2 text-white font-medium text-lg">计重记录</h1>
|
||||
<!-- 导航栏地磅按钮已移除 -->
|
||||
</div>
|
||||
<!-- 内容滚动区域 -->
|
||||
<div class="flex-1 overflow-y-auto hide-scrollbar pb-10">
|
||||
<!-- 搜索栏 -->
|
||||
<div class="h-[3px] bg-blue-500 mx-0"></div>
|
||||
<section class="px-4 pt-4 pb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center bg-white border border-gray-200 shadow-sm rounded-[12px] h-10 px-3 gap-2 flex-1">
|
||||
<iconify-icon class="text-gray-400 text-lg flex-none" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none rounded-lg px-2" placeholder="搜索运单号" type="text"/>
|
||||
<button class="flex-none text-gray-500 btn-active">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none btn-active" onclick="toggleFilterDrawer(true)">
|
||||
<iconify-icon class="text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<!-- 统计标签 -->
|
||||
<div class="px-4 mb-4">
|
||||
<div class="flex gap-2">
|
||||
<!-- 总票数 -->
|
||||
<div class="flex-1 bg-white p-3 rounded-lg border-l-4 border-blue-500 shadow-sm flex flex-col items-center">
|
||||
<span class="text-[11px] text-gray-500 mb-1">总票数</span>
|
||||
<span class="text-xl font-bold text-blue-600">3</span>
|
||||
</div>
|
||||
<!-- 总件数 -->
|
||||
<div class="flex-1 bg-white p-3 rounded-lg border-l-4 border-orange-500 shadow-sm flex flex-col items-center">
|
||||
<span class="text-[11px] text-gray-500 mb-1">总件数</span>
|
||||
<span class="text-xl font-bold text-orange-600">1</span>
|
||||
</div>
|
||||
<!-- 总重量 -->
|
||||
<div class="flex-1 bg-white p-3 rounded-lg border-l-4 border-green-500 shadow-sm flex flex-col items-center">
|
||||
<span class="text-[11px] text-gray-500 mb-1">总重量</span>
|
||||
<span class="text-xl font-bold text-green-600">2</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 记录列表 -->
|
||||
<div class="px-4 flex flex-col gap-4">
|
||||
<!-- 卡片1 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<span class="text-[15px] font-bold text-gray-800">78133363396</span>
|
||||
<!-- 绿色地磅图标,跳转称重明细 -->
|
||||
<button class="btn-active" onclick="window.location.href='weight-detail.html'">
|
||||
<iconify-icon class="text-2xl text-gray-500" icon="mdi:weight"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-gray-500 text-[13px] mb-3">
|
||||
<iconify-icon inline icon="mdi:truck-outline" width="14" height="14"></iconify-icon>
|
||||
<span>代理 : 马道</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-2 bg-gray-50 p-3 rounded-md mb-3">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[11px] text-gray-400">运抵件数</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="text-[14px] font-semibold text-gray-700">85件</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center border-x border-gray-200 px-2">
|
||||
<span class="text-[11px] text-gray-400">运抵重量</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="text-[14px] font-semibold text-gray-700">1560kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[11px] text-gray-400">运抵体积</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="text-[14px] font-semibold text-gray-700">12.5m³</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-gray-500 text-[13px] mb-3">
|
||||
<iconify-icon inline icon="mdi:account-outline" width="16" height="16"></iconify-icon>
|
||||
<span>计重人:张三 · 计重时间:2026-07-05 11:45</span>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="window.location.href='detail.html'">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片2 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<span class="text-[15px] font-bold text-gray-800">78133363398</span>
|
||||
<!-- 绿色地磅图标 -->
|
||||
<button class="btn-active" onclick="window.location.href='weight-detail.html'">
|
||||
<iconify-icon class="text-2xl text-gray-500" icon="mdi:weight"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-gray-500 text-[13px] mb-3">
|
||||
<iconify-icon inline icon="mdi:truck-outline" width="14" height="14"></iconify-icon>
|
||||
<span>代理 : 徽远</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-2 bg-gray-50 p-3 rounded-md mb-3">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[11px] text-gray-400">运抵件数</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="text-[14px] font-semibold text-gray-700">60件</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center border-x border-gray-200 px-2">
|
||||
<span class="text-[11px] text-gray-400">运抵重量</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="text-[14px] font-semibold text-gray-700">920kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[11px] text-gray-400">运抵体积</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="text-[14px] font-semibold text-gray-700">8.1m³</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-gray-500 text-[13px] mb-3">
|
||||
<iconify-icon inline icon="mdi:account-outline" width="16" height="16"></iconify-icon>
|
||||
<span>计重人:张三 · 计重时间:2026-07-05 11:45</span>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="window.location.href='detail.html'">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toast 提示容器 -->
|
||||
<div class="absolute left-1/2 -translate-x-1/2 bottom-20 bg-black/80 text-white text-[13px] px-4 py-2 rounded-full opacity-0 transition-opacity pointer-events-none z-[100]" id="toast"></div>
|
||||
|
||||
<!-- 筛选遮罩层 -->
|
||||
<div class="filter-overlay absolute inset-0 bg-black/40 z-40" id="filterOverlay" onclick="toggleFilterDrawer(false)"></div>
|
||||
|
||||
<!-- 筛选弹窗 -->
|
||||
<div class="filter-modal absolute bottom-0 left-0 right-0 bg-white rounded-t-3xl z-50 p-6 shadow-2xl" id="filter-panel">
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<h2 class="text-center text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
<button class="absolute right-0 top-0 text-gray-400 btn-active" onclick="toggleFilterDrawer(false)">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">入库日期起</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择入库日期" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">入库日期止</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择入库日期" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">代理人</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-[14px]">全部</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">特码</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-[14px]">全部</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">目的站</label>
|
||||
<input type="text" class="w-full bg-gray-50 border border-gray-200 rounded-lg px-3 py-3 text-[14px] outline-none" placeholder="请输入目的站">
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">计重人</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-[14px]">全部</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 pt-2">
|
||||
<button class="flex-1 bg-white border border-gray-300 text-gray-600 py-3.5 rounded-xl font-bold btn-active" onclick="showToast('已重置筛选');toggleFilterDrawer(false)">
|
||||
重置
|
||||
</button>
|
||||
<button class="flex-1 bg-[#2563EB] text-white py-3.5 rounded-xl font-bold shadow-lg btn-active" onclick="toggleFilterDrawer(false);showToast('已应用筛选')">
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-8"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function showToast(message) {
|
||||
const toast = document.getElementById('toast');
|
||||
toast.textContent = message;
|
||||
toast.classList.add('opacity-100');
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('opacity-100');
|
||||
}, 1500);
|
||||
}
|
||||
function toggleFilterDrawer(show) {
|
||||
const overlay = document.getElementById('filterOverlay');
|
||||
const modal = document.getElementById('filter-panel');
|
||||
if (show) {
|
||||
overlay.classList.add('show');
|
||||
modal.classList.add('show');
|
||||
} else {
|
||||
overlay.classList.remove('show');
|
||||
modal.classList.remove('show');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
256
documents/5.5寸手持端设计文件/03_出港计重/weighing.html
Normal file
256
documents/5.5寸手持端设计文件/03_出港计重/weighing.html
Normal file
@@ -0,0 +1,256 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>开始计重 - 货运管理系统</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #F3F4F6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "PingFang SC", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
/* 统一手机外壳容器 */
|
||||
.phone-container {
|
||||
width: 390px;
|
||||
height: 844px;
|
||||
background: #1a1a1a;
|
||||
border-radius: 55px;
|
||||
border: 8px solid #1F2937;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 30px 60px rgba(0,0,0,0.3);
|
||||
position: relative;
|
||||
}
|
||||
/* 手机内部屏幕区域 */
|
||||
.phone-screen {
|
||||
flex: 1;
|
||||
background: #F2F5F9;
|
||||
margin: 0 4px 4px 4px;
|
||||
border-radius: 46px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.no-scrollbar::-webkit-scrollbar { display: none; }
|
||||
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.card-animate {
|
||||
animation: fadeInUp 0.35s ease forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
.card-animate:nth-child(1) { animation-delay: 0s; }
|
||||
.card-animate:nth-child(2) { animation-delay: 0.05s; }
|
||||
.card-animate:nth-child(3) { animation-delay: 0.1s; }
|
||||
.card-animate:nth-child(4) { animation-delay: 0.15s; }
|
||||
@keyframes slideUp {
|
||||
from { transform: translateY(100%); }
|
||||
to { transform: translateY(0); }
|
||||
}
|
||||
.animate-slideUp { animation: slideUp 0.3s ease-out; }
|
||||
input { transition: all 0.2s ease; }
|
||||
input:focus { transition: all 0.2s ease; }
|
||||
::-webkit-scrollbar { display: none; }
|
||||
* { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="phone-container">
|
||||
<div class="phone-screen">
|
||||
|
||||
<!-- ===== ① 顶部连体蓝色区域(状态栏+导航栏) ===== -->
|
||||
<div class="bg-[#2563EB] flex flex-col flex-none">
|
||||
<!-- 手机状态栏 -->
|
||||
<div class="flex items-center justify-between h-[28px] text-white text-[12px] px-6">
|
||||
<span class="text-white font-medium tracking-wide">09:10</span>
|
||||
<div class="flex items-center gap-[5px]">
|
||||
<iconify-icon class="text-white text-[13px]" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-white text-[13px]" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-white text-[15px]" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<header class="text-white h-12 flex items-center px-4">
|
||||
<button class="flex items-center gap-0.5 text-white font-medium text-[15px] active:opacity-70 transition-opacity" onclick="window.location.href='main.html'">
|
||||
<iconify-icon class="text-white" height="24" icon="mdi:chevron-left" width="24"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="flex-1 text-center text-[17px] font-semibold">开始计重</h1>
|
||||
<div class="w-[60px]"></div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<!-- ===== ③ 主内容区(滚动) ===== -->
|
||||
<main class="flex-1 overflow-y-auto no-scrollbar bg-[#F2F5F9] pb-4">
|
||||
<!-- 顶部运单卡片(原样不动) -->
|
||||
<div class="mx-4 mt-4 card-animate">
|
||||
<div class="bg-white rounded-2xl shadow-md shadow-black/5 overflow-hidden relative">
|
||||
<!-- 顶部渐变装饰条 -->
|
||||
<div class="h-1" style="background: linear-gradient(90deg, #2563EB, #60A5FA, #2563EB);"></div>
|
||||
<!-- 内容区 -->
|
||||
<div class="px-5 py-4">
|
||||
<!-- 第一行:运单号 + 状态徽标 -->
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500 animate-pulse"></div>
|
||||
<span class="text-[20px] font-bold text-gray-900 tracking-wider">781333617</span>
|
||||
</div>
|
||||
<span class="text-[11px] font-medium text-blue-600 bg-blue-50 px-2.5 py-1 rounded-full border border-blue-100">国际出港</span>
|
||||
</div>
|
||||
<!-- 分割线 -->
|
||||
<div class="border-t border-gray-50 mb-3"></div>
|
||||
<!-- 统一信息区域:浅灰底圆角容器,包含预配 -->
|
||||
<div class="bg-gray-50/70 rounded-xl px-4 py-3">
|
||||
<!-- 预配信息三列 -->
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="text-center">
|
||||
<p class="text-[10px] text-gray-400 mb-0.5">预配件数</p>
|
||||
<p class="text-[15px] font-bold text-gray-800">85</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[10px] text-gray-400 mb-0.5">预配重量</p>
|
||||
<p class="text-[15px] font-bold text-gray-800">156 kg</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[10px] text-gray-400 mb-0.5">预配体积</p>
|
||||
<p class="text-[15px] font-bold text-gray-800">4.52 m³</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== 统一表单大卡片(字体样式统一为APP风格) ===== -->
|
||||
<div class="mx-4 mt-3 bg-white rounded-2xl shadow-sm shadow-black/5 overflow-hidden card-animate">
|
||||
<!-- 1. 通道号(必填)-->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100 cursor-pointer active:bg-gray-50" onclick="openChannelPicker()">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">通道号<span class="text-red-500 ml-0.5">*</span></span>
|
||||
<span class="flex-1 text-[14px] text-gray-500 text-left" id="channelDisplay">请选择通道号</span>
|
||||
<iconify-icon class="text-gray-400 shrink-0" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
<!-- 2. 代理人 -->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100 cursor-pointer active:bg-gray-50" onclick="openAgentPicker()">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">代理人</span>
|
||||
<span class="flex-1 text-[14px] text-gray-500 text-left" id="agentDisplay">请选择代理人</span>
|
||||
<iconify-icon class="text-gray-400 shrink-0" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
<!-- 3. 特码 -->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100 cursor-pointer active:bg-gray-50" onclick="openCodePicker()">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">特码</span>
|
||||
<span class="flex-1 text-[14px] text-gray-500 text-left" id="codeDisplay">请选择特码</span>
|
||||
<iconify-icon class="text-gray-400 shrink-0" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
<!-- 4. 运抵件数 -->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">运抵件数</span>
|
||||
<input class="flex-1 text-[14px] text-gray-800 bg-transparent border-none outline-none text-left" placeholder="请输入运抵件数" type="text"/>
|
||||
</div>
|
||||
<!-- 5. 运抵重量 -->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">运抵重量</span>
|
||||
<input class="flex-1 text-[14px] text-gray-800 bg-transparent border-none outline-none text-left" type="text" value="0"/>
|
||||
</div>
|
||||
<!-- 6. 运抵体积 -->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">运抵体积</span>
|
||||
<input class="flex-1 text-[14px] text-gray-800 bg-transparent border-none outline-none text-left" type="text" value="0.00"/>
|
||||
</div>
|
||||
<!-- 7. 托盘数量 -->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">托盘数量</span>
|
||||
<input class="flex-1 text-[14px] text-gray-800 bg-transparent border-none outline-none text-left" placeholder="请输入托盘数量" type="text"/>
|
||||
</div>
|
||||
<!-- 8. 托盘自重 -->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">托盘自重</span>
|
||||
<input class="flex-1 text-[14px] text-gray-800 bg-transparent border-none outline-none text-left" value="18" placeholder="请输入托盘自重" type="text"/>
|
||||
</div>
|
||||
|
||||
<!-- 航班信息 -->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">航班日期</span>
|
||||
<span class="flex-1 text-[14px] text-gray-800 text-left">20260703</span>
|
||||
</div>
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">航班号</span>
|
||||
<span class="flex-1 text-[14px] text-gray-800 text-left">MU1111</span>
|
||||
</div>
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">航程</span>
|
||||
<span class="flex-1 text-[14px] text-gray-800 text-left">HFE-LAX</span>
|
||||
</div>
|
||||
|
||||
<!-- 业务类型 -->
|
||||
<div class="flex items-center h-12 px-4 border-b border-gray-100 cursor-pointer active:bg-gray-50" onclick="openBizPicker()">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">业务类型</span>
|
||||
<span class="flex-1 text-[14px] text-gray-800 text-left" id="bizDisplay">普通货物运输</span>
|
||||
<iconify-icon class="text-gray-400 shrink-0" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
|
||||
<!-- 备注 -->
|
||||
<div class="flex items-start h-auto min-h-[80px] px-4 py-3 border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0 pt-1">备注</span>
|
||||
<textarea class="flex-1 text-[14px] text-gray-800 bg-transparent rounded-lg py-2 h-[60px] resize-none outline-none text-left" placeholder="请输入备注信息"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<div class="px-4 mt-4 mb-2">
|
||||
<div class="flex gap-3">
|
||||
<button class="flex-1 h-[52px] rounded-2xl text-[17px] font-semibold active:opacity-90 transition-all text-blue-600" style="background: #fff; border: 2px solid #2563EB;">
|
||||
分托计重
|
||||
</button>
|
||||
<button class="flex-1 h-[52px] rounded-2xl bg-[#2563EB] text-white text-[17px] font-semibold active:opacity-90 shadow-lg shadow-blue-200 transition-all" onclick="submitWeight()">
|
||||
完成计重
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- iOS 底部指示条 -->
|
||||
<div class="h-5 bg-[#F2F5F9] flex justify-center items-center flex-none">
|
||||
<div class="w-[134px] h-[5px] bg-gray-300 rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openChannelPicker() {
|
||||
const options = ['A-01', 'A-02', 'B-01', 'B-02', 'C-01'];
|
||||
document.getElementById('channelDisplay').innerText = "A-01";
|
||||
document.getElementById('channelDisplay').classList.remove('text-gray-500');
|
||||
document.getElementById('channelDisplay').classList.add('text-gray-800');
|
||||
}
|
||||
|
||||
function openAgentPicker() {
|
||||
document.getElementById('agentDisplay').innerText = "上海中远海运物流";
|
||||
document.getElementById('agentDisplay').classList.remove('text-gray-500');
|
||||
document.getElementById('agentDisplay').classList.add('text-gray-800');
|
||||
}
|
||||
|
||||
function openCodePicker() {
|
||||
document.getElementById('codeDisplay').innerText = "GEN (普货)";
|
||||
document.getElementById('codeDisplay').classList.remove('text-gray-500');
|
||||
document.getElementById('codeDisplay').classList.add('text-gray-800');
|
||||
}
|
||||
|
||||
function openBizPicker() {
|
||||
}
|
||||
|
||||
function submitWeight() {
|
||||
alert("提交成功");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
306
documents/5.5寸手持端设计文件/03_出港计重/weight-detail.html
Normal file
306
documents/5.5寸手持端设计文件/03_出港计重/weight-detail.html
Normal file
@@ -0,0 +1,306 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>计重明细</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #F3F4F6;
|
||||
font-size: 13px;
|
||||
}
|
||||
.safe-bottom {
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.btn-active:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-200 min-h-screen flex items-center justify-center p-4">
|
||||
<div class="relative w-[390px] h-[844px] bg-[#1a1a1a] rounded-[55px] shadow-2xl border-[6px] border-[#333] flex flex-col overflow-hidden">
|
||||
<div class="flex-1 bg-white mt-0 mb-1 mx-1 rounded-[46px] overflow-hidden flex flex-col relative">
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<div class="bg-[#2563EB] text-white h-11 px-7 flex items-end pb-2 justify-between text-[14px] font-medium z-50">
|
||||
<span>09:41</span>
|
||||
<div class="flex items-center gap-1.5 pb-0.5">
|
||||
<iconify-icon icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 列表页 -->
|
||||
<div id="listPage" class="flex-1 flex flex-col overflow-hidden relative bg-gray-100">
|
||||
<header class="relative bg-[#2563EB] text-white flex items-center h-12 px-3 sticky top-0 z-50">
|
||||
<div class="flex items-center btn-active" onclick="window.location.href='records.html'">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-medium text-white -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="absolute left-1/2 -translate-x-1/2 text-lg font-bold text-white">计重明细</h1>
|
||||
</header>
|
||||
|
||||
<div class="flex-1 overflow-y-auto p-4 no-scrollbar">
|
||||
<!-- 运单卡片(与 781333617 样式统一) -->
|
||||
<div class="bg-white rounded-2xl shadow-md shadow-black/5 overflow-hidden relative mb-3">
|
||||
<!-- 顶部渐变装饰条 -->
|
||||
<div class="h-1" style="background: linear-gradient(90deg, #2563EB, #60A5FA, #2563EB);"></div>
|
||||
<!-- 内容区 -->
|
||||
<div class="px-5 py-4">
|
||||
<!-- 第一行:运单号 + 状态徽标 -->
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500 animate-pulse"></div>
|
||||
<span class="text-[20px] font-bold text-gray-900 tracking-wider">78133363396</span>
|
||||
</div>
|
||||
<span class="text-[11px] font-medium text-blue-600 bg-blue-50 px-2.5 py-1 rounded-full border border-blue-100">国际出港</span>
|
||||
</div>
|
||||
<!-- 分割线 -->
|
||||
<div class="border-t border-gray-50 mb-3"></div>
|
||||
<!-- 统一信息区域:浅灰底圆角容器 -->
|
||||
<div class="bg-gray-50/70 rounded-xl px-4 py-3">
|
||||
<div class="grid grid-cols-3 gap-2 text-center">
|
||||
<div>
|
||||
<p class="text-[10px] text-gray-400 mb-0.5">总件数</p>
|
||||
<p class="text-[15px] font-bold text-gray-800">85</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[10px] text-gray-400 mb-0.5">总重量(kg)</p>
|
||||
<p class="text-[15px] font-bold text-gray-800">1560</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[10px] text-gray-400 mb-0.5">托盘总重(kg)</p>
|
||||
<p class="text-[15px] font-bold text-gray-800">54</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 左滑卡片1 -->
|
||||
<div class="swipe-item bg-white rounded-xl shadow-sm mb-3 overflow-hidden relative" data-id="1">
|
||||
<div class="swipe-content p-4 relative z-10 bg-white">
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-600">运抵件数</span>
|
||||
<span class="font-medium piece">20</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-600">运抵重量(kg)</span>
|
||||
<span class="font-medium weight">320</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-500">托盘自重(kg)</span>
|
||||
<span class="text-gray-600">18</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-500">计重人</span>
|
||||
<span class="text-gray-600 user">张三</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-500">计重时间</span>
|
||||
<span class="text-gray-600 time">2026-07-05 10:02:15</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swipe-action absolute top-0 right-0 bottom-0 w-[60px] bg-[#2563EB] flex items-center justify-center z-0">
|
||||
<button class="text-white text-sm" onclick="goEdit(1)">修改</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片2 -->
|
||||
<div class="swipe-item bg-white rounded-xl shadow-sm mb-3 overflow-hidden relative" data-id="2">
|
||||
<div class="swipe-content p-4 relative z-10 bg-white">
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-600">运抵件数</span>
|
||||
<span class="font-medium piece">35</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-600">运抵重量(kg)</span>
|
||||
<span class="font-medium weight">590</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-500">托盘自重(kg)</span>
|
||||
<span class="text-gray-600">18</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-500">计重人</span>
|
||||
<span class="text-gray-600 user">张三</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-500">计重时间</span>
|
||||
<span class="text-gray-600 time">2026-07-05 10:25:30</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swipe-action absolute top-0 right-0 bottom-0 w-[60px] bg-[#2563EB] flex items-center justify-center z-0">
|
||||
<button class="text-white text-sm" onclick="goEdit(2)">修改</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片3 -->
|
||||
<div class="swipe-item bg-white rounded-xl shadow-sm mb-3 overflow-hidden relative" data-id="3">
|
||||
<div class="swipe-content p-4 relative z-10 bg-white">
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-600">运抵件数</span>
|
||||
<span class="font-medium piece">30</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-600">运抵重量(kg)</span>
|
||||
<span class="font-medium weight">650</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-500">托盘自重(kg)</span>
|
||||
<span class="text-gray-600">18</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-500">计重人</span>
|
||||
<span class="text-gray-600 user">张三</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-2">
|
||||
<span class="text-gray-500">计重时间</span>
|
||||
<span class="text-gray-600 time">2026-07-05 11:45:08</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swipe-action absolute top-0 right-0 bottom-0 w-[60px] bg-[#2563EB] flex items-center justify-center z-0">
|
||||
<button class="text-white text-sm" onclick="goEdit(3)">修改</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 修改记录页面(完全在手机容器内) -->
|
||||
<div id="editPage" class="absolute inset-0 bg-gray-100 z-[60] flex flex-col hidden">
|
||||
<div class="bg-[#2563EB] text-white h-11 px-7 flex items-end pb-2 justify-between text-[14px] font-medium z-50 flex-none">
|
||||
<span>09:41</span>
|
||||
<div class="flex items-center gap-1.5 pb-0.5">
|
||||
<iconify-icon icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<header class="relative bg-[#2563EB] text-white flex items-center h-12 px-3 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="closeEditPage()">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-medium text-white -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="absolute left-1/2 -translate-x-1/2 text-lg font-bold text-white">修改记录</h1>
|
||||
</header>
|
||||
<div class="flex-1 overflow-y-auto p-4 no-scrollbar">
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-hidden px-4">
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">运抵件数<span class="text-red-500 ml-0.5">*</span></span>
|
||||
<input id="edit_piece" class="flex-1 bg-transparent outline-none text-gray-800 text-[14px]">
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">运抵重量(kg)<span class="text-red-500 ml-0.5">*</span></span>
|
||||
<input id="edit_weight" class="flex-1 bg-transparent outline-none text-gray-800 text-[14px]">
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">托盘自重(kg)<span class="text-red-500 ml-0.5">*</span></span>
|
||||
<input value="18" class="flex-1 bg-transparent outline-none text-gray-800 text-[14px]">
|
||||
</div>
|
||||
<div class="flex items-center py-[14px] border-b border-gray-100">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">计重人</span>
|
||||
<input id="edit_user" disabled class="flex-1 bg-transparent outline-none text-gray-500 text-[14px]">
|
||||
</div>
|
||||
<div class="flex items-center py-[14px]">
|
||||
<span class="w-[90px] text-[13px] font-medium text-[#6B7280] shrink-0">计重时间</span>
|
||||
<input id="edit_time" disabled class="flex-1 bg-transparent outline-none text-gray-500 text-[14px]">
|
||||
</div>
|
||||
</div>
|
||||
<button class="w-full mt-8 bg-[#2563EB] text-white py-3.5 rounded-[10px] font-bold text-[16px] shadow-lg shadow-blue-500/30 btn-active" onclick="saveEdit()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentEditId = null
|
||||
|
||||
// 左滑效果(鼠标+触摸)
|
||||
document.querySelectorAll('.swipe-item').forEach(item => {
|
||||
const content = item.querySelector('.swipe-content')
|
||||
let startX = 0
|
||||
let isDragging = false
|
||||
|
||||
item.addEventListener('mousedown', e => {
|
||||
isDragging = true
|
||||
startX = e.clientX
|
||||
e.preventDefault()
|
||||
})
|
||||
|
||||
window.addEventListener('mousemove', e => {
|
||||
if (!isDragging) return
|
||||
const dx = startX - e.clientX
|
||||
if (dx > 0) {
|
||||
const off = Math.min(dx, 60)
|
||||
content.style.transform = `translateX(-${off}px)`
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('mouseup', () => {
|
||||
if (!isDragging) return
|
||||
isDragging = false
|
||||
const dx = startX - event.clientX
|
||||
if (dx > 30) {
|
||||
content.style.transform = 'translateX(-60px)'
|
||||
} else {
|
||||
content.style.transform = ''
|
||||
}
|
||||
})
|
||||
|
||||
item.addEventListener('touchstart', e => {
|
||||
startX = e.touches[0].clientX
|
||||
})
|
||||
|
||||
item.addEventListener('touchmove', e => {
|
||||
const dx = startX - e.touches[0].clientX
|
||||
if (dx > 0) {
|
||||
const off = Math.min(dx, 60)
|
||||
content.style.transform = `translateX(-${off}px)`
|
||||
}
|
||||
})
|
||||
|
||||
item.addEventListener('touchend', () => {
|
||||
const dx = startX - event.changedTouches[0].clientX
|
||||
content.style.transform = dx > 30 ? 'translateX(-60px)' : ''
|
||||
})
|
||||
})
|
||||
|
||||
function goEdit(id) {
|
||||
currentEditId = id
|
||||
const item = document.querySelector(`.swipe-item[data-id="${id}"]`)
|
||||
document.getElementById('edit_piece').value = item.querySelector('.piece').innerText
|
||||
document.getElementById('edit_weight').value = item.querySelector('.weight').innerText
|
||||
document.getElementById('edit_user').value = item.querySelector('.user').innerText
|
||||
document.getElementById('edit_time').value = item.querySelector('.time').innerText
|
||||
document.getElementById('listPage').classList.add('hidden')
|
||||
document.getElementById('editPage').classList.remove('hidden')
|
||||
}
|
||||
|
||||
function closeEditPage() {
|
||||
document.getElementById('editPage').classList.add('hidden')
|
||||
document.getElementById('listPage').classList.remove('hidden')
|
||||
}
|
||||
|
||||
function saveEdit() {
|
||||
const p = document.getElementById('edit_piece').value
|
||||
const w = document.getElementById('edit_weight').value
|
||||
const item = document.querySelector(`.swipe-item[data-id="${currentEditId}"]`)
|
||||
item.querySelector('.piece').innerText = p
|
||||
item.querySelector('.weight').innerText = w
|
||||
closeEditPage()
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
535
documents/5.5寸手持端设计文件/04_出港运抵/main.html
Normal file
535
documents/5.5寸手持端设计文件/04_出港运抵/main.html
Normal file
@@ -0,0 +1,535 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>出港运抵</title>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- Iconify -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/iconify-icon@2.1.0/dist/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
body {
|
||||
background-color: #f3f6f8;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translate(-50%, 10px); }
|
||||
to { opacity: 1; transform: translate(-50%, 0); }
|
||||
}
|
||||
.animate-fadeIn {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<div class="phone-shell w-[375px] h-[812px] bg-[#F5F5F7] overflow-hidden flex flex-col relative rounded-[48px] border-[10px] border-gray-900">
|
||||
<div class="bg-[#2563EB] flex-none">
|
||||
<div class="h-[44px] flex justify-between items-center px-8 text-white">
|
||||
<span class="text-[15px] font-semibold">09:41</span>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-lg" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-lg" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-xl" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-[44px] flex items-center px-4 relative">
|
||||
<div class="flex items-center text-white cursor-pointer" onclick="window.history.back()">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span class="text-sm">返回</span>
|
||||
</div>
|
||||
<div class="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||
<h1 class="text-white text-lg font-bold">出港运抵</h1>
|
||||
</div>
|
||||
<div class="ml-auto w-10"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar">
|
||||
<section class="px-4 pt-4 pb-3 bg-white">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center bg-white border border-gray-200 shadow-sm rounded-[12px] h-10 px-3 gap-2 flex-1">
|
||||
<iconify-icon class="text-gray-400 text-lg flex-none" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none rounded-lg px-2" placeholder="搜索运单号" type="text"/>
|
||||
<button class="flex-none text-blue-500">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="showFilterSheet2()">
|
||||
<iconify-icon class="text-lg" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="flex bg-white border-b sticky top-0 z-20">
|
||||
<button class="tab-btn flex-1 py-3 text-sm font-bold border-b-2 transition-all text-[#1F2937] border-[#1F2937]" id="tab-pending" onclick="switchTab('pending')">
|
||||
待运抵 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5" id="count-pending">1</span>
|
||||
</button>
|
||||
<button class="tab-btn flex-1 py-3 text-sm font-bold border-b-2 transition-all text-gray-400 border-transparent" id="tab-shipped" onclick="switchTab('shipped')">
|
||||
已运抵 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5" id="count-shipped">4</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="px-5 pb-5 pt-3">
|
||||
<div class="flex flex-col items-center justify-center py-16 text-gray-400 hidden" id="empty-pending">
|
||||
<svg class="w-16 h-16 text-gray-300 mb-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
|
||||
<path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||
</svg>
|
||||
<p class="text-sm">暂无待运抵的运单</p>
|
||||
</div>
|
||||
|
||||
<!-- 待运抵卡片(长按显示补充按钮) -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-4 uld-card relative" data-status="pending">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="radio-btn border-2 border-gray-300 rounded-full w-5 h-5 inline-flex items-center justify-center flex-none cursor-pointer"></span>
|
||||
<div class="flex items-baseline">
|
||||
<span class="text-sm font-bold text-gray-900">78133363396</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-red-500 text-white text-[11px] font-medium px-2.5 py-0.5 rounded-full">提前运抵</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-gray-500 text-[13px] mt-3">
|
||||
<div><span>代理:</span><span>马道</span></div>
|
||||
<div><span>件数:</span><span>444</span></div>
|
||||
<div><span>重量:</span><span>150kg</span></div>
|
||||
<div><span>航班日期:</span><span>20260713</span></div>
|
||||
<div><span>航班号:</span><span>MU2033</span></div>
|
||||
<div><span>航程:</span><span>HFE-LAX</span></div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="w-full bg-[#EFF6FF] text-[#2563EB] text-xs py-2 rounded-[6px] font-medium h-8 flex items-center justify-center gap-2" onclick="event.stopPropagation(); window.location.href='suborder.html'">
|
||||
查看分单
|
||||
<span class="text-[#2563EB] text-[10px] font-bold">3</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 长按弹出操作层 -->
|
||||
<div class="card-action-overlay absolute inset-0 bg-black/40 rounded-2xl z-10 hidden flex items-center justify-center pointer-events-none">
|
||||
<div class="flex items-center gap-4 pointer-events-auto shipped-actions hidden">
|
||||
<button class="bg-amber-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-amber-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); this.closest('.card-action-overlay').classList.add('hidden'); setTimeout(() => showResetStatusSheet(), 200)">重置</button>
|
||||
<button class="bg-emerald-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-emerald-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); window.location.href='receipt.html'">回执</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已运抵卡片1 -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-4 uld-card relative" data-status="shipped" style="display: none;">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-bold text-gray-900">78133363397</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-indigo-100 text-indigo-500 text-[11px] font-medium px-2.5 py-0.5 rounded-full">W</span>
|
||||
<span class="bg-green-100 text-green-600 text-[11px] font-medium px-2 py-0.5 rounded-md">11</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-gray-500 text-[13px] mt-3">
|
||||
<div><span>代理:</span><span>徽远</span></div>
|
||||
<div><span>件数:</span><span>300</span></div>
|
||||
<div><span>重量:</span><span>120kg</span></div>
|
||||
<div><span>航班日期:</span><span>20260713</span></div>
|
||||
<div><span>航班号:</span><span>MU2033</span></div>
|
||||
<div><span>航程:</span><span>HFE-LAX</span></div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="w-full bg-[#EFF6FF] text-[#2563EB] text-xs py-2 rounded-[6px] font-medium h-8 flex items-center justify-center gap-2" onclick="event.stopPropagation(); window.location.href='suborder.html'">
|
||||
查看分单
|
||||
<span class="text-[#2563EB] text-[10px] font-bold">2</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-action-overlay absolute inset-0 bg-black/40 rounded-2xl z-10 hidden flex items-center justify-center pointer-events-none">
|
||||
<div class="flex items-center gap-4 pointer-events-auto pending-actions hidden">
|
||||
<button class="bg-purple-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-purple-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); this.closest('.card-action-overlay').classList.add('hidden'); window.location.href='supplement.html'">补充</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 pointer-events-auto shipped-actions hidden">
|
||||
<button class="bg-amber-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-amber-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); this.closest('.card-action-overlay').classList.add('hidden'); setTimeout(() => showResetStatusSheet(), 200)">重置</button>
|
||||
<button class="bg-emerald-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-emerald-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); window.location.href='receipt.html'">回执</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已运抵卡片2 -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-4 uld-card relative" data-status="shipped" style="display: none;">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-bold text-gray-900">78133363398</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] font-medium px-2.5 py-0.5 rounded-full">01</span>
|
||||
<span class="bg-green-100 text-green-600 text-[11px] font-medium px-2 py-0.5 rounded-md">11</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-gray-500 text-[13px] mt-3">
|
||||
<div><span>代理:</span><span>中外运</span></div>
|
||||
<div><span>件数:</span><span>130</span></div>
|
||||
<div><span>重量:</span><span>130kg</span></div>
|
||||
<div><span>航班日期:</span><span>20260713</span></div>
|
||||
<div><span>航班号:</span><span>MU2033</span></div>
|
||||
<div><span>航程:</span><span>HFE-LAX</span></div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="w-full bg-[#EFF6FF] text-[#2563EB] text-xs py-2 rounded-[6px] font-medium h-8 flex items-center justify-center gap-2" onclick="event.stopPropagation(); window.location.href='suborder.html'">
|
||||
查看分单
|
||||
<span class="text-[#2563EB] text-[10px] font-bold">2</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-action-overlay absolute inset-0 bg-black/40 rounded-2xl z-10 hidden flex items-center justify-center pointer-events-none">
|
||||
<div class="flex items-center gap-4 pointer-events-auto pending-actions hidden">
|
||||
<button class="bg-purple-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-purple-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); this.closest('.card-action-overlay').classList.add('hidden'); window.location.href='supplement.html'">补充</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 pointer-events-auto shipped-actions hidden">
|
||||
<button class="bg-amber-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-amber-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); this.closest('.card-action-overlay').classList.add('hidden'); setTimeout(() => showResetStatusSheet(), 200)">重置</button>
|
||||
<button class="bg-emerald-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-emerald-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); window.location.href='receipt.html'">回执</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已运抵卡片3:78133363399 -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-4 uld-card relative" data-status="shipped" style="display: none;">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-bold text-gray-900">78133363399</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-amber-100 text-amber-600 text-[11px] font-medium px-2.5 py-0.5 rounded-full">E</span>
|
||||
<span class="bg-green-100 text-green-600 text-[11px] font-medium px-2 py-0.5 rounded-md">11</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-gray-500 text-[13px] mt-3">
|
||||
<div><span>代理:</span><span>MU</span></div>
|
||||
<div><span>件数:</span><span>300</span></div>
|
||||
<div><span>重量:</span><span>110kg</span></div>
|
||||
<div><span>航班日期:</span><span>20260713</span></div>
|
||||
<div><span>航班号:</span><span>MU2034</span></div>
|
||||
<div><span>航程:</span><span>HFE-LAX</span></div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button class="w-full bg-[#EFF6FF] text-[#2563EB] text-xs py-2 rounded-[6px] font-medium h-8 flex items-center justify-center gap-2" onclick="event.stopPropagation(); window.location.href='https://modao.cc/agent-py/workspace/6a50a1993333bc5bbd8ee054/waybill-arrival.html/suborder.html?render_html=true'">
|
||||
查看分单
|
||||
<span class="text-[#2563EB] text-[10px] font-bold">1</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 卡片操作菜单(默认隐藏) -->
|
||||
<div class="card-action-overlay absolute inset-0 bg-black/40 rounded-2xl z-10 hidden flex items-center justify-center pointer-events-none">
|
||||
<!-- 已运抵按钮容器(显示重置+回执) -->
|
||||
<div class="flex items-center gap-4 pointer-events-auto shipped-actions hidden">
|
||||
<button class="bg-amber-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-amber-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); this.closest('.card-action-overlay').classList.add('hidden'); setTimeout(() => showResetStatusSheet(), 200)">
|
||||
重置
|
||||
</button>
|
||||
<button class="bg-emerald-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-emerald-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); window.location.href='https://modao.cc/agent-py/workspace/6a50a1993333bc5bbd8ee054/waybill-arrival.html/receipt.html?render_html=true'">
|
||||
回执
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="flex-none bg-white border-t border-gray-100" id="bottom-action-bar">
|
||||
<div class="flex items-center justify-between px-5 py-3">
|
||||
<div class="flex items-center gap-2 select-all cursor-pointer">
|
||||
<span class="radio-select-all w-5 h-5 rounded-full border-2 border-gray-300 bg-white flex items-center justify-center"></span>
|
||||
<span class="text-sm text-gray-700 font-medium">全选</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 bottom-action-container">
|
||||
<span class="text-[12px] text-[#9CA3AF]">已选 <span id="selected-count">0</span> 项</span>
|
||||
<button class="bg-[#2563EB] text-white text-[15px] font-bold px-5 py-2 rounded-[10px] shadow-lg shadow-blue-500/25" onclick="alert('运抵申报')">申报</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pb-2 flex justify-center">
|
||||
<div class="w-32 h-1.5 bg-gray-200 rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选弹窗 -->
|
||||
<div class="absolute inset-0 bg-black/40 z-50 hidden" id="filter-sheet2-overlay" onclick="hideFilterSheet2()"></div>
|
||||
<div class="absolute bottom-0 left-0 right-0 bg-white rounded-t-2xl shadow-2xl z-50 transform translate-y-full transition-transform duration-300 ease-in-out" id="filter-sheet2">
|
||||
<div class="px-5 pt-5 pb-8">
|
||||
<div class="flex items-center justify-center mb-6 relative">
|
||||
<h3 class="text-base font-bold text-gray-900">筛选条件</h3>
|
||||
<button class="absolute right-0 w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center" onclick="hideFilterSheet2()">
|
||||
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24">
|
||||
<line x1="18" x2="6" y1="6" y2="18"></line>
|
||||
<line x1="6" x2="18" y1="6" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="text-sm text-gray-500 font-medium mb-1.5 block">航班日期</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3">
|
||||
<iconify-icon class="text-gray-400 text-lg flex-none" icon="mdi:calendar"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none px-2" placeholder="选择日期" type="text" value="2026-07-10"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-sm text-gray-500 font-medium mb-1.5 block">航班号</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3">
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none" placeholder="请输入航班号" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-sm text-gray-500 font-medium mb-1.5 block">代理</label>
|
||||
<select class="w-full bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 text-[14px] text-gray-800 outline-none appearance-none bg-[url('data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%239CA3AF%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%226%209%2012%2015%2018%209%22%3E%3C%2Fpolyline%3E%3C%2Fsvg%3E')] bg-[length:14px] bg-[right_12px_center] bg-no-repeat">
|
||||
<option disabled selected value="">请选择代理</option>
|
||||
<option value="马道">马道</option>
|
||||
<option value="徽远">徽远</option>
|
||||
<option value="中外运">中外运</option>
|
||||
<option value="MU">MU</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 mt-6">
|
||||
<button class="flex-1 py-3 text-sm font-medium text-gray-500 bg-gray-100 rounded-xl" onclick="hideFilterSheet2()">重置</button>
|
||||
<button class="flex-1 py-3 text-sm font-medium text-white bg-[#2563EB] rounded-xl" onclick="hideFilterSheet2()">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 重置状态弹窗 -->
|
||||
<div class="absolute inset-0 bg-black/40 z-50 hidden" id="reset-sheet-overlay" onclick="hideResetStatusSheet()"></div>
|
||||
<div class="absolute bottom-0 left-0 right-0 bg-white rounded-t-2xl shadow-2xl z-50 transform translate-y-full transition-transform duration-300 ease-in-out" id="reset-sheet">
|
||||
<div class="px-5 pt-5 pb-8">
|
||||
<div class="flex items-center justify-center mb-6 relative">
|
||||
<h3 class="text-base font-bold text-gray-900">重置状态</h3>
|
||||
<button class="absolute right-0 w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center" onclick="hideResetStatusSheet()">
|
||||
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24">
|
||||
<line x1="18" x2="6" y1="6" y2="18"></line>
|
||||
<line x1="6" x2="18" y1="6" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-sm text-gray-500 font-medium mb-1.5 block">状态</label>
|
||||
<select class="w-full bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 text-[14px] text-gray-800 outline-none appearance-none bg-[url('data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%239CA3AF%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%226%209%2012%2015%2018%209%22%3E%3C%2Fpolyline%3E%3C%2Fsvg%3E')] bg-[length:14px] bg-[right_12px_center] bg-no-repeat" id="reset-status-select">
|
||||
<option disabled selected value="">请选择状态</option>
|
||||
<option value="">空</option>
|
||||
<option value="01">01</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="w-full mt-6 py-3 text-sm font-medium text-white bg-[#2563EB] rounded-xl" onclick="confirmResetStatus()">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentTab = 'pending';
|
||||
let longPressTimer = null;
|
||||
let isLongPress = false;
|
||||
|
||||
// Toast提示
|
||||
function showToast(message) {
|
||||
const existingToast = document.querySelector('.custom-toast');
|
||||
if (existingToast) existingToast.remove();
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'custom-toast fixed bottom-32 left-1/2 -translate-x-1/2 bg-gray-800 text-white text-sm px-5 py-3 rounded-xl shadow-lg z-50 animate-fadeIn';
|
||||
toast.textContent = message;
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(() => {
|
||||
toast.style.opacity = '0';
|
||||
toast.style.transition = 'opacity 0.3s';
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// 清空所有选中
|
||||
function resetAllSelections() {
|
||||
document.querySelectorAll('.radio-btn').forEach(radio => toggleRadio(radio, false));
|
||||
const selectAllRadio = document.querySelector('.radio-select-all');
|
||||
toggleRadio(selectAllRadio, false);
|
||||
}
|
||||
|
||||
// 切换标签页
|
||||
function switchTab(tab) {
|
||||
currentTab = tab;
|
||||
const tabPending = document.getElementById('tab-pending');
|
||||
const tabShipped = document.getElementById('tab-shipped');
|
||||
const bottomBar = document.getElementById('bottom-action-bar');
|
||||
const emptyTip = document.getElementById('empty-pending');
|
||||
|
||||
// 切换tab样式
|
||||
if (tab === 'pending') {
|
||||
tabPending.classList.remove('text-gray-400', 'border-transparent');
|
||||
tabPending.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabShipped.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabShipped.classList.add('text-gray-400', 'border-transparent');
|
||||
bottomBar.classList.remove('hidden');
|
||||
} else {
|
||||
tabPending.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabPending.classList.add('text-gray-400', 'border-transparent');
|
||||
tabShipped.classList.remove('text-gray-400', 'border-transparent');
|
||||
tabShipped.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
bottomBar.classList.add('hidden');
|
||||
}
|
||||
|
||||
// 切换卡片显示
|
||||
const allCards = document.querySelectorAll('.uld-card');
|
||||
let showCount = 0;
|
||||
allCards.forEach(card => {
|
||||
if (card.dataset.status === tab) {
|
||||
card.style.display = 'block';
|
||||
showCount++;
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
emptyTip.classList.toggle('hidden', showCount > 0);
|
||||
resetAllSelections();
|
||||
updateSelectCount();
|
||||
}
|
||||
|
||||
// 更新选中数量
|
||||
function updateSelectCount() {
|
||||
const list = document.querySelectorAll(`.uld-card[data-status="${currentTab}"] .radio-btn.bg-blue-500`);
|
||||
document.getElementById('selected-count').textContent = list.length;
|
||||
syncSelectAllStatus();
|
||||
}
|
||||
|
||||
// 同步全选按钮样式
|
||||
function syncSelectAllStatus() {
|
||||
const allCards = document.querySelectorAll(`.uld-card[data-status="${currentTab}"]`);
|
||||
const selectedCards = document.querySelectorAll(`.uld-card[data-status="${currentTab}"] .radio-btn.bg-blue-500`);
|
||||
const selectAllBtn = document.querySelector('.radio-select-all');
|
||||
if (allCards.length > 0 && allCards.length === selectedCards.length) {
|
||||
selectAllBtn.classList.add('bg-blue-500', 'border-blue-500');
|
||||
selectAllBtn.classList.remove('border-gray-300', 'bg-white');
|
||||
selectAllBtn.innerHTML = `<svg class="w-3 h-3 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`;
|
||||
} else {
|
||||
selectAllBtn.classList.remove('bg-blue-500', 'border-blue-500');
|
||||
selectAllBtn.classList.add('border-gray-300', 'bg-white');
|
||||
selectAllBtn.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 单选按钮切换
|
||||
function toggleRadio(el, force) {
|
||||
const isChecked = force != null ? force : !el.classList.contains('bg-blue-500');
|
||||
if (isChecked) {
|
||||
el.classList.add('bg-blue-500', 'border-blue-500');
|
||||
el.classList.remove('border-gray-300', 'bg-white');
|
||||
el.innerHTML = `<svg class="w-3 h-3 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`;
|
||||
} else {
|
||||
el.classList.remove('bg-blue-500', 'border-blue-500');
|
||||
el.classList.add('border-gray-300', 'bg-white');
|
||||
el.innerHTML = '';
|
||||
}
|
||||
updateSelectCount();
|
||||
}
|
||||
|
||||
// 单个单选点击
|
||||
document.querySelectorAll('.radio-btn').forEach(radio => {
|
||||
radio.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
toggleRadio(radio);
|
||||
};
|
||||
});
|
||||
|
||||
// 全选按钮
|
||||
document.querySelector('.select-all').onclick = function() {
|
||||
const currentCards = document.querySelectorAll(`.uld-card[data-status="${currentTab}"] .radio-btn`);
|
||||
const allSelected = Array.from(currentCards).every(item => item.classList.contains('bg-blue-500'));
|
||||
currentCards.forEach(radio => toggleRadio(radio, !allSelected));
|
||||
};
|
||||
|
||||
// ========== 完整长按逻辑(待运抵弹出补充,已运抵弹出重置回执) ==========
|
||||
document.querySelectorAll('.uld-card').forEach(card => {
|
||||
// 触摸长按
|
||||
card.addEventListener('touchstart', (e) => {
|
||||
// 点击单选框、按钮不触发长按
|
||||
if (e.target.closest('.radio-btn') || e.target.closest('button')) return;
|
||||
isLongPress = false;
|
||||
longPressTimer = setTimeout(() => {
|
||||
isLongPress = true;
|
||||
showCardAction(card);
|
||||
}, 600);
|
||||
});
|
||||
card.addEventListener('touchend', () => clearTimeout(longPressTimer));
|
||||
card.addEventListener('touchmove', () => clearTimeout(longPressTimer));
|
||||
|
||||
// 鼠标长按兼容
|
||||
card.addEventListener('mousedown', (e) => {
|
||||
if (e.target.closest('.radio-btn') || e.target.closest('button')) return;
|
||||
isLongPress = false;
|
||||
longPressTimer = setTimeout(() => {
|
||||
isLongPress = true;
|
||||
showCardAction(card);
|
||||
}, 600);
|
||||
});
|
||||
card.addEventListener('mouseup', () => clearTimeout(longPressTimer));
|
||||
card.addEventListener('mouseleave', () => clearTimeout(longPressTimer));
|
||||
});
|
||||
|
||||
// 展示卡片操作面板
|
||||
function showCardAction(card) {
|
||||
// 关闭所有卡片操作层
|
||||
document.querySelectorAll('.card-action-overlay').forEach(overlay => overlay.classList.add('hidden'));
|
||||
const overlay = card.querySelector('.card-action-overlay');
|
||||
const pendingAction = overlay.querySelector('.pending-actions');
|
||||
const shippedAction = overlay.querySelector('.shipped-actions');
|
||||
pendingAction.classList.add('hidden');
|
||||
shippedAction.classList.add('hidden');
|
||||
// 根据状态展示对应按钮
|
||||
if (card.dataset.status === 'pending') {
|
||||
pendingAction.classList.remove('hidden');
|
||||
} else {
|
||||
shippedAction.classList.remove('hidden');
|
||||
}
|
||||
overlay.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// 点击空白区域关闭操作弹窗
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.uld-card') && !e.target.closest('.card-action-overlay')) {
|
||||
document.querySelectorAll('.card-action-overlay').forEach(overlay => overlay.classList.add('hidden'));
|
||||
}
|
||||
});
|
||||
|
||||
// 筛选弹窗
|
||||
function showFilterSheet2() {
|
||||
const overlay = document.getElementById('filter-sheet2-overlay');
|
||||
const sheet = document.getElementById('filter-sheet2');
|
||||
overlay.classList.remove('hidden');
|
||||
setTimeout(() => sheet.classList.remove('translate-y-full'), 10);
|
||||
}
|
||||
function hideFilterSheet2() {
|
||||
const overlay = document.getElementById('filter-sheet2-overlay');
|
||||
const sheet = document.getElementById('filter-sheet2');
|
||||
sheet.classList.add('translate-y-full');
|
||||
setTimeout(() => overlay.classList.add('hidden'), 300);
|
||||
}
|
||||
|
||||
// 重置状态弹窗
|
||||
function showResetStatusSheet() {
|
||||
document.getElementById('reset-sheet-overlay').classList.remove('hidden');
|
||||
document.getElementById('reset-sheet').classList.remove('translate-y-full');
|
||||
}
|
||||
function hideResetStatusSheet() {
|
||||
document.getElementById('reset-sheet-overlay').classList.add('hidden');
|
||||
document.getElementById('reset-sheet').classList.add('translate-y-full');
|
||||
}
|
||||
function confirmResetStatus() {
|
||||
const selectVal = document.getElementById('reset-status-select').value;
|
||||
if (!selectVal) {
|
||||
alert('请选择状态');
|
||||
return;
|
||||
}
|
||||
alert('已选择状态:' + (selectVal === '' ? '置空' : selectVal));
|
||||
hideResetStatusSheet();
|
||||
}
|
||||
|
||||
// 页面初始化
|
||||
switchTab('pending');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
73
documents/5.5寸手持端设计文件/04_出港运抵/receipt.html
Normal file
73
documents/5.5寸手持端设计文件/04_出港运抵/receipt.html
Normal file
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>回执内容</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
</head>
|
||||
<body class="bg-gray-100 min-h-screen flex items-center justify-center font-sans">
|
||||
<div class="phone-shell w-[375px] h-[812px] bg-[#F5F5F7] rounded-[48px] border-[10px] border-gray-900 overflow-hidden relative flex flex-col shadow-2xl">
|
||||
<!-- 状态栏 -->
|
||||
<div class="bg-blue-600 text-white text-xs px-6 py-1 flex justify-between items-center shrink-0 h-[44px]">
|
||||
<span class="text-[15px] font-semibold">09:41</span>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
<iconify-icon class="text-lg" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-lg" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-xl" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<div class="bg-blue-600 px-4 py-3 flex items-center gap-2 text-white shrink-0 h-[44px]">
|
||||
<a class="text-white text-sm flex items-center gap-1" href="main.html">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
返回
|
||||
</a>
|
||||
<span class="flex-1 text-center text-white font-bold text-base">回执内容</span>
|
||||
<div class="w-12"></div>
|
||||
</div>
|
||||
<!-- 内容列表 -->
|
||||
<div class="p-4 space-y-3 flex-1 overflow-y-auto no-scrollbar">
|
||||
<!-- 回执 1 -->
|
||||
<div class="bg-white rounded-lg p-4 shadow-sm">
|
||||
<p class="text-gray-700 text-sm leading-relaxed">
|
||||
<span class="inline-block w-2 h-2 bg-blue-500 rounded-full mr-2.5 align-middle"></span>
|
||||
单一窗口回执内容:45201 空运出口运抵报告传输成功。比对结果为:运抵正常。
|
||||
</p>
|
||||
<p class="text-gray-400 text-xs mt-2 ml-[18px]">2026-07-10 11:03:38</p>
|
||||
</div>
|
||||
<!-- 回执 2 -->
|
||||
<div class="bg-white rounded-lg p-4 shadow-sm">
|
||||
<p class="text-gray-700 text-sm leading-relaxed">
|
||||
<span class="inline-block w-2 h-2 bg-blue-500 rounded-full mr-2.5 align-middle"></span>
|
||||
单一窗口回执内容:45202 空运出口运抵报告传输成功。比对结果为:运抵正常。
|
||||
</p>
|
||||
<p class="text-gray-400 text-xs mt-2 ml-[18px]">2026-07-10 11:02:15</p>
|
||||
</div>
|
||||
<!-- 回执 3 -->
|
||||
<div class="bg-white rounded-lg p-4 shadow-sm">
|
||||
<p class="text-gray-700 text-sm leading-relaxed">
|
||||
<span class="inline-block w-2 h-2 bg-blue-500 rounded-full mr-2.5 align-middle"></span>
|
||||
单一窗口回执内容:45203 空运出口运抵报告传输成功。比对结果为:运抵正常。
|
||||
</p>
|
||||
<p class="text-gray-400 text-xs mt-2 ml-[18px]">2026-07-10 10:58:42</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Home Indicator -->
|
||||
<div class="h-8 w-full flex items-center justify-center shrink-0">
|
||||
<div class="w-32 h-1.5 bg-gray-300 rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
413
documents/5.5寸手持端设计文件/04_出港运抵/suborder.html
Normal file
413
documents/5.5寸手持端设计文件/04_出港运抵/suborder.html
Normal file
@@ -0,0 +1,413 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>分单明细 - 出港运抵</title>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<!-- Iconify -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.phone-shell {
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-200 h-screen flex items-center justify-center font-sans">
|
||||
<!-- 手机外壳容器 -->
|
||||
<div class="phone-shell w-[375px] h-[812px] bg-[#F5F5F7] rounded-[48px] border-[10px] border-gray-900 overflow-hidden flex flex-col relative">
|
||||
<!-- 顶部蓝色背景区域 -->
|
||||
<div class="bg-[#1A73E8] flex-none">
|
||||
<!-- 状态栏 -->
|
||||
<div class="h-11 w-full flex justify-between items-center px-8 text-white">
|
||||
<span class="text-sm font-medium">09:41</span>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon icon="mingcute:signal-fill" width="16"></iconify-icon>
|
||||
<iconify-icon icon="mingcute:wifi-line" width="16"></iconify-icon>
|
||||
<iconify-icon class="rotate-90" icon="mingcute:battery-fill" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<div class="h-[44px] px-4 flex items-center justify-between text-white">
|
||||
<button class="flex items-center gap-1 text-sm" onclick="window.location.href='main.html'">
|
||||
<iconify-icon icon="lucide:chevron-left" width="20"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="text-lg font-bold">分单明细</h1>
|
||||
<div class="w-10"></div> <!-- 占位 -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- 搜索栏 -->
|
||||
<section class="px-5 pt-4 pb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center bg-white border border-gray-200 shadow-sm rounded-[12px] h-10 px-3 gap-2 flex-1">
|
||||
<iconify-icon class="text-gray-400 text-lg flex-none" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none rounded-lg px-2" placeholder="搜索运单号" type="text"/>
|
||||
<button class="flex-none text-blue-500">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="showFilterSheet()">
|
||||
<iconify-icon class="text-lg" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<!-- 可滚动内容区 -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar px-5 pb-24 pt-2">
|
||||
<!-- 分单列表内容 -->
|
||||
<div class="space-y-4" id="suborder-list">
|
||||
<!-- 分单1 -->
|
||||
<div class="suborder-card bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-4 cursor-pointer transition-all relative active:scale-[0.98]" data-id="10001" onclick="toggleSuborderSelect(this)">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="select-indicator border-2 border-gray-300 bg-white rounded-full w-5 h-5 inline-flex items-center justify-center flex-none cursor-pointer transition-colors"></span>
|
||||
<span class="text-sm font-bold text-gray-900">10001</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-indigo-100 text-indigo-500 text-[11px] font-medium px-2.5 py-0.5 rounded-full">W</span>
|
||||
<span class="bg-green-100 text-green-600 text-[11px] font-medium px-2 py-0.5 rounded-md">11</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-gray-500 text-[13px] mt-3">
|
||||
<div>
|
||||
<span>件数:</span><span>150</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>重量:</span><span>50kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片操作菜单(默认隐藏) -->
|
||||
<div class="card-action-overlay absolute inset-0 bg-black/40 rounded-2xl z-10 hidden flex items-center justify-center pointer-events-none">
|
||||
<div class="flex items-center gap-4 pointer-events-auto">
|
||||
<!-- 重置按钮 -->
|
||||
<button class="bg-amber-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-amber-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); this.closest('.card-action-overlay').classList.add('hidden'); setTimeout(() => showResetStatusSheet(), 200)">
|
||||
重置
|
||||
</button>
|
||||
<!-- 回执按钮 -->
|
||||
<button class="bg-emerald-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-emerald-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); window.location.href='receipt.html'">
|
||||
回执
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分单2 -->
|
||||
<div class="suborder-card bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-4 cursor-pointer transition-all relative active:scale-[0.98]" data-id="10002" onclick="toggleSuborderSelect(this)">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="select-indicator border-2 border-gray-300 bg-white rounded-full w-5 h-5 inline-flex items-center justify-center flex-none cursor-pointer transition-colors"></span>
|
||||
<span class="text-sm font-bold text-gray-900">10002</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-amber-100 text-amber-600 text-[11px] font-medium px-2.5 py-0.5 rounded-full">E</span>
|
||||
<span class="bg-green-100 text-green-600 text-[11px] font-medium px-2 py-0.5 rounded-md">11</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-gray-500 text-[13px] mt-3">
|
||||
<div>
|
||||
<span>件数:</span><span>120</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>重量:</span><span>40kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片操作菜单(默认隐藏) -->
|
||||
<div class="card-action-overlay absolute inset-0 bg-black/40 rounded-2xl z-10 hidden flex items-center justify-center pointer-events-none">
|
||||
<div class="flex items-center gap-4 pointer-events-auto">
|
||||
<!-- 重置按钮 -->
|
||||
<button class="bg-amber-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-amber-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); this.closest('.card-action-overlay').classList.add('hidden'); setTimeout(() => showResetStatusSheet(), 200)">
|
||||
重置
|
||||
</button>
|
||||
<!-- 回执按钮 -->
|
||||
<button class="bg-emerald-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-emerald-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); window.location.href='receipt.html'">
|
||||
回执
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分单3 -->
|
||||
<div class="suborder-card bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-4 cursor-pointer transition-all relative active:scale-[0.98]" data-id="10003" onclick="toggleSuborderSelect(this)">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="select-indicator border-2 border-gray-300 bg-white rounded-full w-5 h-5 inline-flex items-center justify-center flex-none cursor-pointer transition-colors"></span>
|
||||
<span class="text-sm font-bold text-gray-900">10003</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] font-medium px-2.5 py-0.5 rounded-full">01</span>
|
||||
<span class="bg-green-100 text-green-600 text-[11px] font-medium px-2 py-0.5 rounded-md">11</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-gray-500 text-[13px] mt-3">
|
||||
<div>
|
||||
<span>件数:</span><span>174</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>重量:</span><span>60kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片操作菜单(默认隐藏) -->
|
||||
<div class="card-action-overlay absolute inset-0 bg-black/40 rounded-2xl z-10 hidden flex items-center justify-center pointer-events-none">
|
||||
<div class="flex items-center gap-4 pointer-events-auto">
|
||||
<!-- 重置按钮 -->
|
||||
<button class="bg-amber-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-amber-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); this.closest('.card-action-overlay').classList.add('hidden'); setTimeout(() => showResetStatusSheet(), 200)">
|
||||
重置
|
||||
</button>
|
||||
<!-- 回执按钮 -->
|
||||
<button class="bg-emerald-500 text-white text-[10px] font-bold w-14 h-14 rounded-full shadow-lg shadow-emerald-500/30 flex items-center justify-center leading-tight" onclick="event.stopPropagation(); window.location.href='receipt.html'">
|
||||
回执
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="flex-none bg-white border-t border-gray-100">
|
||||
<div class="flex items-center justify-between px-5 py-3">
|
||||
<div class="flex items-center gap-2 select-all-container cursor-pointer" onclick="toggleSuborderSelectAll(this)">
|
||||
<span class="radio-select-all w-5 h-5 rounded-full border-2 border-gray-300 bg-white flex items-center justify-center transition-colors"></span>
|
||||
<span class="text-sm text-gray-700 font-medium">全选</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-[12px] text-[#9CA3AF]" id="suborder-count">已选 0 / 3 项</span>
|
||||
<button class="bg-[#2563EB] text-white text-[15px] font-bold px-5 py-2 rounded-[10px] shadow-lg shadow-blue-500/25 active:scale-95 transition-transform" onclick="alert('申报成功')">申报</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选弹框遮罩 -->
|
||||
<div class="absolute inset-0 bg-black/40 z-30 hidden" id="filter-sheet-overlay" onclick="hideFilterSheet()"></div>
|
||||
<!-- 筛选弹框 -->
|
||||
<div class="absolute bottom-0 left-0 right-0 bg-white rounded-t-2xl shadow-2xl z-40 transform translate-y-full transition-transform duration-300 ease-in-out" id="filter-sheet">
|
||||
<div class="px-5 pt-5 pb-8">
|
||||
<!-- 标题 -->
|
||||
<div class="flex items-center justify-center mb-6 relative">
|
||||
<h3 class="text-base font-bold text-gray-900">筛选条件</h3>
|
||||
<button class="absolute right-0 w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center" onclick="hideFilterSheet()">
|
||||
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24">
|
||||
<line x1="18" x2="6" y1="6" y2="18"></line>
|
||||
<line x1="6" x2="18" y1="6" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 筛选条件区域 -->
|
||||
<div class="space-y-4">
|
||||
<!-- 航班日期 -->
|
||||
<div>
|
||||
<label class="text-sm text-gray-500 font-medium mb-1.5 block">航班日期</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3">
|
||||
<iconify-icon class="text-gray-400 text-lg flex-none" icon="mdi:calendar"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none px-2" placeholder="选择日期" type="text" value="2026-07-10"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 航班号 -->
|
||||
<div>
|
||||
<label class="text-sm text-gray-500 font-medium mb-1.5 block">航班号</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3">
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none" placeholder="请输入航班号" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 代理(已改为下拉框) -->
|
||||
<div>
|
||||
<label class="text-sm text-gray-500 font-medium mb-1.5 block">代理</label>
|
||||
<div class="relative">
|
||||
<select class="w-full bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 text-[14px] text-gray-800 outline-none appearance-none focus:border-blue-500 focus:bg-white transition-all">
|
||||
<option disabled selected value="">请选择代理</option>
|
||||
<option value="马道">马道</option>
|
||||
<option value="徽远">徽远</option>
|
||||
<option value="中外运">中外运</option>
|
||||
</select>
|
||||
<iconify-icon class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-xl" icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分单号 -->
|
||||
<div>
|
||||
<label class="text-sm text-gray-500 font-medium mb-1.5 block">分单号</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3">
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none" placeholder="请输入分单号" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部操作按钮 -->
|
||||
<div class="flex gap-3 mt-6">
|
||||
<button class="flex-1 py-3 text-sm font-medium text-gray-500 bg-gray-100 rounded-xl" onclick="hideFilterSheet()">重置</button>
|
||||
<button class="flex-1 py-3 text-sm font-medium text-white bg-[#2563EB] rounded-xl" onclick="hideFilterSheet()">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Home Indicator 底部横条 -->
|
||||
<div class="h-8 w-full flex justify-center items-center">
|
||||
<div class="w-32 h-1.5 bg-black rounded-full opacity-20"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 重置状态弹框 -->
|
||||
<div class="absolute inset-0 bg-black/40 z-30 hidden" id="reset-sheet-overlay" onclick="hideResetStatusSheet()"></div>
|
||||
<div class="absolute bottom-0 left-0 right-0 bg-white rounded-t-2xl shadow-2xl z-40 transform translate-y-full transition-transform duration-300 ease-in-out" id="reset-sheet">
|
||||
<div class="px-5 pt-5 pb-8">
|
||||
<!-- 标题 -->
|
||||
<div class="flex items-center justify-center mb-6 relative">
|
||||
<h3 class="text-base font-bold text-gray-900">重置状态</h3>
|
||||
<button class="absolute right-0 w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center" onclick="hideResetStatusSheet()">
|
||||
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24">
|
||||
<line x1="18" x2="6" y1="6" y2="18"></line>
|
||||
<line x1="6" x2="18" y1="6" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 状态下拉选择 -->
|
||||
<div>
|
||||
<label class="text-sm text-gray-500 font-medium mb-1.5 block">状态</label>
|
||||
<select class="w-full bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 text-[14px] text-gray-800 outline-none appearance-none bg-[url('data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%202000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%239CA3AF%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%226%209%2012%2015%2018%209%22%3E%3C%2Fpolyline%3E%3C%2Fsvg%3E')] bg-[length:14px] bg-[right_12px_center] bg-no-repeat" id="reset-status-select">
|
||||
<option disabled="" selected="" value="">请选择状态</option>
|
||||
<option value="">空</option>
|
||||
<option value="01">01</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- 确认按钮 -->
|
||||
<button class="w-full mt-6 py-3 text-sm font-medium text-white bg-[#2563EB] rounded-xl" onclick="confirmResetStatus()">
|
||||
确定
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 单选分单卡片
|
||||
function toggleSuborderSelect(card) {
|
||||
if (window.isLongPressing) return;
|
||||
card.classList.toggle('selected');
|
||||
const indicator = card.querySelector('.select-indicator');
|
||||
if (card.classList.contains('selected')) {
|
||||
indicator.classList.remove('border-gray-300', 'bg-white');
|
||||
indicator.classList.add('bg-blue-500', 'border-blue-500');
|
||||
indicator.innerHTML = `<svg class="w-3 h-3 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`;
|
||||
} else {
|
||||
indicator.classList.remove('bg-blue-500', 'border-blue-500');
|
||||
indicator.classList.add('border-gray-300', 'bg-white');
|
||||
indicator.innerHTML = '';
|
||||
}
|
||||
updateSuborderCount();
|
||||
}
|
||||
|
||||
// 全选/取消全选
|
||||
function toggleSuborderSelectAll(btn) {
|
||||
const allCards = document.querySelectorAll('.suborder-card');
|
||||
const isAllSelected = Array.from(allCards).every(card => card.classList.contains('selected'));
|
||||
if (isAllSelected) {
|
||||
allCards.forEach(card => {
|
||||
card.classList.remove('selected');
|
||||
const ci = card.querySelector('.select-indicator');
|
||||
ci.classList.remove('bg-blue-500', 'border-blue-500');
|
||||
ci.classList.add('border-gray-300', 'bg-white');
|
||||
ci.innerHTML = '';
|
||||
});
|
||||
} else {
|
||||
allCards.forEach(card => {
|
||||
card.classList.add('selected');
|
||||
const ci = card.querySelector('.select-indicator');
|
||||
ci.classList.remove('border-gray-300', 'bg-white');
|
||||
ci.classList.add('bg-blue-500', 'border-blue-500');
|
||||
ci.innerHTML = `<svg class="w-3 h-3 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`;
|
||||
});
|
||||
}
|
||||
updateSuborderCount();
|
||||
}
|
||||
|
||||
// 更新底部选中数量
|
||||
function updateSuborderCount() {
|
||||
const selected = document.querySelectorAll('.suborder-card.selected').length;
|
||||
const total = document.querySelectorAll('.suborder-card').length;
|
||||
document.getElementById('suborder-count').innerText = `已选 ${selected} / ${total} 项`;
|
||||
updateSelectAllState(selected, total);
|
||||
}
|
||||
|
||||
// 同步全选按钮状态
|
||||
function updateSelectAllState(selectedCount, totalCount) {
|
||||
const allBtn = document.querySelector('.radio-select-all');
|
||||
if (selectedCount === totalCount && totalCount > 0) {
|
||||
allBtn.classList.remove('border-gray-300', 'bg-white');
|
||||
allBtn.classList.add('bg-blue-500', 'border-blue-500');
|
||||
allBtn.innerHTML = `<svg class="w-3 h-3 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`;
|
||||
} else {
|
||||
allBtn.classList.remove('bg-blue-500', 'border-blue-500');
|
||||
allBtn.classList.add('border-gray-300', 'bg-white');
|
||||
allBtn.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 筛选弹窗打开
|
||||
function showFilterSheet() {
|
||||
const overlay = document.getElementById('filter-sheet-overlay');
|
||||
const sheet = document.getElementById('filter-sheet');
|
||||
overlay.classList.remove('hidden');
|
||||
setTimeout(() => {
|
||||
sheet.classList.remove('translate-y-full');
|
||||
sheet.classList.add('translate-y-0');
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// 筛选弹窗关闭
|
||||
function hideFilterSheet() {
|
||||
const overlay = document.getElementById('filter-sheet-overlay');
|
||||
const sheet = document.getElementById('filter-sheet');
|
||||
sheet.classList.add('translate-y-full');
|
||||
sheet.classList.remove('translate-y-0');
|
||||
setTimeout(() => {
|
||||
overlay.classList.add('hidden');
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// 卡片长按弹出操作菜单
|
||||
window.isLongPressing = false;
|
||||
document.querySelectorAll('.suborder-card').forEach(card => {
|
||||
let timer;
|
||||
// 鼠标按下
|
||||
card.addEventListener('mousedown', e => {
|
||||
if (e.target.closest('.select-indicator') || e.target.closest('button')) return;
|
||||
window.isLongPressing = false;
|
||||
timer = setTimeout(() => {
|
||||
window.isLongPressing = true;
|
||||
showSuborderActionMenu(card);
|
||||
}, 600);
|
||||
});
|
||||
card.addEventListener('mouseup', () => { clearTimeout(timer); setTimeout(()=>{window.isLongPressing=false},50) });
|
||||
card.addEventListener('mouseleave', () => { clearTimeout(timer); window.isLongPressing=false });
|
||||
// 触屏兼容
|
||||
card.addEventListener('touchstart', e => {
|
||||
if (e.target.closest('.select-indicator') || e.target.closest('button')) return;
|
||||
window.isLongPressing = false;
|
||||
timer = setTimeout(() => {
|
||||
window.isLongPressing = true;
|
||||
showSuborderActionMenu(card);
|
||||
}, 600);
|
||||
});
|
||||
card.addEventListener('touchend', () => { clearTimeout(timer); setTimeout(()=>{window.isLongPressing=false},50) });
|
||||
card.addEventListener('touchmove', () => { clearTimeout(timer); window.isLongPressing=false });
|
||||
});
|
||||
|
||||
function showSuborderActionMenu(card) {
|
||||
document.querySelectorAll('.card-action-overlay').forEach(o => o.classList.add('hidden'));
|
||||
const overlay = card.querySelector('.card-action-overlay');
|
||||
if (overlay) overlay.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// 重置状态弹窗
|
||||
function showResetStatusSheet() {
|
||||
document.getElementById('reset-sheet-overlay').classList.remove('hidden');
|
||||
document.getElementById('reset-sheet').classList.remove('translate-y-full');
|
||||
}
|
||||
function hideResetStatusSheet() {
|
||||
document.getElementById('reset-sheet-overlay').classList.add('hidden');
|
||||
document.getElementById('reset-sheet').classList.add('translate-y-full');
|
||||
}
|
||||
function confirmResetStatus() {
|
||||
const s = document.getElementById('reset-status-select');
|
||||
if (!s.value && s.selectedIndex===0) { alert('请选择状态'); return; }
|
||||
alert(s.value==='' ? '已选择状态: 置空' : '已选择状态: '+s.value);
|
||||
hideResetStatusSheet();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
175
documents/5.5寸手持端设计文件/04_出港运抵/supplement.html
Normal file
175
documents/5.5寸手持端设计文件/04_出港运抵/supplement.html
Normal file
@@ -0,0 +1,175 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>补充信息</title>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<!-- Iconify -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
body {
|
||||
background-color: #f3f4f6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-200">
|
||||
<!-- Phone Shell -->
|
||||
<div class="phone-shell w-[375px] h-[812px] bg-[#F5F5F7] overflow-hidden flex flex-col relative rounded-[48px] border-[10px] border-gray-900 shadow-2xl">
|
||||
<!-- Status Bar & Nav Bar Container -->
|
||||
<div class="bg-[#1A73E8] flex-none">
|
||||
<!-- Status Bar -->
|
||||
<div class="h-[44px] flex justify-between items-center px-8 text-white">
|
||||
<span class="text-[15px] font-semibold">09:41</span>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<iconify-icon class="text-lg" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-lg" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-xl" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Navigation Bar -->
|
||||
<div class="h-[44px] flex items-center px-4 relative">
|
||||
<div class="flex items-center text-white cursor-pointer" onclick="window.location.href='main.html'">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span class="text-sm">返回</span>
|
||||
</div>
|
||||
<div class="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||
<h1 class="text-white text-lg font-bold">补充信息</h1>
|
||||
</div>
|
||||
<div class="ml-auto w-10"></div> <!-- Placeholder for right side -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- Content Area -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar p-4 space-y-4">
|
||||
<!-- 收货人信息 -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<div class="w-1.5 h-4 bg-[#1A73E8] rounded-full"></div>
|
||||
<h3 class="text-sm font-bold text-gray-900">收货人信息</h3>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">收货人名称</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 focus-within:border-blue-500 focus-within:bg-white transition-all">
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none" placeholder="请输入收货人名称" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">国家代码</label>
|
||||
<div class="relative">
|
||||
<select class="w-full bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 text-[14px] text-gray-800 outline-none appearance-none focus:border-blue-500 focus:bg-white transition-all">
|
||||
<option disabled="" value="">请选择</option>
|
||||
<option selected="" value="US">US - 美国</option>
|
||||
<option value="CN">CN - 中国</option>
|
||||
<option value="JP">JP - 日本</option>
|
||||
<option value="UK">UK - 英国</option>
|
||||
<option value="DE">DE - 德国</option>
|
||||
<option value="FR">FR - 法国</option>
|
||||
</select>
|
||||
<iconify-icon class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-xl" icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">通讯方式</label>
|
||||
<div class="relative">
|
||||
<select class="w-full bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 text-[14px] text-gray-800 outline-none appearance-none focus:border-blue-500 focus:bg-white transition-all">
|
||||
<option disabled="" selected="" value="">请选择</option>
|
||||
<option value="phone">电话</option>
|
||||
<option value="email">邮件</option>
|
||||
<option value="fax">传真</option>
|
||||
</select>
|
||||
<iconify-icon class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-xl" icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">联系号码</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 focus-within:border-blue-500 focus-within:bg-white transition-all">
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none" placeholder="请输入联系号码" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">地址</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 focus-within:border-blue-500 focus-within:bg-white transition-all">
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none" placeholder="请输入地址" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 发货人信息 -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<div class="w-1.5 h-4 bg-[#1A73E8] rounded-full"></div>
|
||||
<h3 class="text-sm font-bold text-gray-900">发货人信息</h3>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">发货人名称</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 focus-within:border-blue-500 focus-within:bg-white transition-all">
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none" placeholder="请输入发货人名称" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">国家代码</label>
|
||||
<div class="relative">
|
||||
<select class="w-full bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 text-[14px] text-gray-800 outline-none appearance-none focus:border-blue-500 focus:bg-white transition-all">
|
||||
<option disabled="" value="">请选择</option>
|
||||
<option selected="" value="US">US - 美国</option>
|
||||
<option value="CN">CN - 中国</option>
|
||||
<option value="JP">JP - 日本</option>
|
||||
<option value="UK">UK - 英国</option>
|
||||
<option value="DE">DE - 德国</option>
|
||||
<option value="FR">FR - 法国</option>
|
||||
</select>
|
||||
<iconify-icon class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-xl" icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">通讯方式</label>
|
||||
<div class="relative">
|
||||
<select class="w-full bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 text-[14px] text-gray-800 outline-none appearance-none focus:border-blue-500 focus:bg-white transition-all">
|
||||
<option disabled="" selected="" value="">请选择</option>
|
||||
<option value="phone">电话</option>
|
||||
<option value="email">邮件</option>
|
||||
<option value="fax">传真</option>
|
||||
</select>
|
||||
<iconify-icon class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-xl" icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">联系号码</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 focus-within:border-blue-500 focus-within:bg-white transition-all">
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none" placeholder="请输入联系号码" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[13px] text-gray-500 font-medium mb-1.5 block">地址</label>
|
||||
<div class="flex items-center bg-gray-50 border border-gray-200 rounded-xl h-10 px-3 focus-within:border-blue-500 focus-within:bg-white transition-all">
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none" placeholder="请输入地址" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部确定按钮 -->
|
||||
<div class="px-5 py-4 shrink-0">
|
||||
<button class="w-full bg-[#2563EB] text-white text-[15px] font-bold py-3.5 rounded-xl shadow-lg shadow-blue-500/30 active:scale-[0.98] transition-all" onclick="alert('补充信息已提交'); window.location.href='main.html'">
|
||||
确定
|
||||
</button>
|
||||
<!-- Home Indicator -->
|
||||
<div class="mt-4 flex justify-center">
|
||||
<div class="w-32 h-1.5 bg-gray-200 rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
668
documents/5.5寸手持端设计文件/05_出港移库/出港移库.html
Normal file
668
documents/5.5寸手持端设计文件/05_出港移库/出港移库.html
Normal file
@@ -0,0 +1,668 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>出港移库 - 移动端原型</title>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<!-- Iconify -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'PingFang SC';
|
||||
src: local('PingFang SC');
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
/* 同步参考页面body背景色 */
|
||||
background-color: #e2e8f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
/* 模拟手机容器 - 同步参考底色 */
|
||||
.phone-container {
|
||||
width: 375px;
|
||||
height: 812px;
|
||||
background-color: #f5f6fa;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
border: 8px solid #1a1a1a;
|
||||
border-radius: 40px;
|
||||
}
|
||||
/* 隐藏滚动条但保留滚动 */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
/* 页面切换动画 */
|
||||
.page {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
.page.active {
|
||||
display: flex;
|
||||
}
|
||||
/* 筛选面板弹出动画 */
|
||||
#filter-panel {
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
#filter-panel.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.overlay {
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
.overlay.show {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
/* 按钮按压效果 */
|
||||
.btn-active:active {
|
||||
opacity: 0.7;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
/* 复选框样式 */
|
||||
.custom-checkbox {
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #cbd5e1;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.custom-checkbox:checked {
|
||||
background-color: #2563EB;
|
||||
border-color: #2563EB;
|
||||
}
|
||||
.custom-checkbox:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.custom-checkbox-round {
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #cbd5e1;
|
||||
border-radius: 50%;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.custom-checkbox-round:checked {
|
||||
background-color: #2563EB;
|
||||
border-color: #2563EB;
|
||||
}
|
||||
.custom-checkbox-round:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="phone-container">
|
||||
<!-- 状态栏 -->
|
||||
<div class="h-10 w-full flex justify-between items-center px-8 flex-none bg-[#2563EB] text-white">
|
||||
<span class="text-xs font-semibold">09:41</span>
|
||||
<div class="flex gap-1 items-center">
|
||||
<iconify-icon icon="mdi:signal" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 列表首页 -->
|
||||
<div class="page active" id="page-home">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="bg-[#2563EB] text-white h-12 flex items-center justify-between px-4 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="alert('返回首页')">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-bold -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium">出港移库</h1>
|
||||
<div class="w-16"></div>
|
||||
</div>
|
||||
|
||||
<!-- 滚动内容区域 -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar pb-24">
|
||||
<!-- 搜索栏 -->
|
||||
<section class="px-4 pt-4 pb-3 bg-white">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center bg-white border border-gray-200 shadow-sm rounded-[12px] h-10 px-3 gap-2 flex-1">
|
||||
<iconify-icon class="text-gray-400 text-lg flex-none" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none rounded-lg px-2" placeholder="搜索运单号" type="text"/>
|
||||
<button class="flex-none text-blue-500">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="toggleFilterDrawer(true)">
|
||||
<iconify-icon class="text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Tab切换 -->
|
||||
<div class="flex bg-white border-b sticky top-0 z-20">
|
||||
<button class="flex-1 py-3 text-sm font-bold text-[#1F2937] border-b-2 border-[#1F2937] transition-all" id="tab-pending" onclick="switchTab('pending')">
|
||||
待移库 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5">3</span>
|
||||
</button>
|
||||
<button class="flex-1 py-3 text-sm font-bold text-gray-400 border-b-2 border-transparent transition-all" id="tab-shipped" onclick="switchTab('shipped')">
|
||||
已移库 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5">3</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 待移库列表 -->
|
||||
<div class="p-3 pt-4 space-y-3" id="list-pending">
|
||||
<!-- 卡片1 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">78133363573</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">待移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">NOR</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">100件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">200kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-NKG-LAX</span>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363573', status: '待移库', agent: 'NOR', pieces: '100', weight: '200', goodsName: '电子元器件', carrier: 'MU', route: 'HFE-NKG-LAX', location: 'A区-03货位', code: 'NOR', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片2 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">78133363574</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">待移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">CTA</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">85件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">150kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-PEK-LAX</span>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363574', status: '待移库', agent: 'CTA', pieces: '85', weight: '150', goodsName: '服装鞋帽', carrier: 'CA', route: 'HFE-PEK-LAX', location: 'B区-05货位', code: 'NOR', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片3 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">78133363575</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">待移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">DHL</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">200件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">450kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-NKG-LAX</span>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363575', status: '待移库', agent: 'DHL', pieces: '200', weight: '450', goodsName: '精密模具', carrier: '3U', route: 'HFE-NKG-LAX', location: 'C区-01货位', code: 'NOR', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已移库列表 -->
|
||||
<div class="p-3 pt-4 space-y-3 hidden" id="list-shipped">
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">78133363001</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">NOR</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">50件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">80kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-NKG-LAX</span>
|
||||
</div>
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363001', status: '已移库', agent: 'NOR', pieces: '50', weight: '80', pickupNo: 'TH20260701001', goodsName: '精密仪器', carrier: 'MU', route: 'HFE-NKG-LAX', location: 'B区-05货位', code: 'NOR', counterTime: '2026-07-05 10:30', counterPerson: '张三', outboundTime: '2026-07-05 11:45', outboundPerson: '张三'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">78133363002</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">CTA</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">120件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">300kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-PEK-LAX</span>
|
||||
</div>
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363002', status: '已移库', agent: 'CTA', pieces: '120', weight: '300', pickupNo: 'TH20260702002', goodsName: '鲜花绿植', carrier: 'CA', route: 'HFE-PEK-LAX', location: 'A区-08货位', code: 'NOR', counterTime: '2026-07-06 09:15', counterPerson: '王五', outboundTime: '2026-07-06 10:30', outboundPerson: '赵六'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">78133363003</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">DHL</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">75件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">160kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-NKG-LAX</span>
|
||||
</div>
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363003', status: '已移库', agent: 'DHL', pieces: '75', weight: '160', pickupNo: 'TH20260703003', goodsName: '生鲜食品', carrier: '3U', route: 'HFE-NKG-LAX', location: 'D区-02货位', code: 'NOR', counterTime: '2026-07-06 14:00', counterPerson: '钱七', outboundTime: '2026-07-06 15:20', outboundPerson: '孙八'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="absolute bottom-0 w-full bg-white border-t border-gray-200 z-40 flex flex-col pt-4 pb-4" id="bottom-toolbar">
|
||||
<div class="px-4 flex items-center w-full">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="custom-checkbox-round" id="select-all-cb" onclick="toggleSelectAll(this.checked)" type="checkbox"/>
|
||||
<label class="text-[13px] text-[#1F2937] font-medium cursor-pointer" for="select-all-cb">全选</label>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-[12px] text-[#9CA3AF]">已选 <span id="selected-count">0</span> 项</span>
|
||||
<button class="bg-[#2563EB] text-white text-[15px] font-bold px-5 py-2 rounded-[10px] shadow-lg shadow-blue-500/25 btn-active" onclick="alert('移库操作')">
|
||||
移库
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 运单详情页面 -->
|
||||
<div class="page" id="page-detail">
|
||||
<div class="bg-[#2563EB] text-white h-12 flex items-center justify-between px-4 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="switchPage('page-home')">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-bold -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium">运单详情</h1>
|
||||
<div class="w-16"></div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar pb-8 p-4">
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-hidden border border-gray-100">
|
||||
<div class="bg-[#2563EB] px-4 py-3 flex justify-between items-center text-white">
|
||||
<span class="font-bold text-[17px] leading-tight" id="detail-awb">781-22222620</span>
|
||||
<div class="flex items-center text-[13px] font-normal opacity-90">
|
||||
<span class="w-2 h-2 bg-green-400 rounded-full mr-1.5"></span>
|
||||
<span id="detail-time">2026-05-10 08:30</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-[#EF4444] text-white text-[11px] px-1.5 py-0.5 rounded leading-tight font-medium">国际进港</span>
|
||||
<span class="text-[13px] font-normal text-[#666666] border-l border-gray-300 pl-2">普通货物运输</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-[22px] font-bold text-[#111827] mb-3" id="detail-goods-name">电子元器件</div>
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-3">
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">件数</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-count">55</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">重量</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-weight">622.00 kg</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">特码</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-code">NOR</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">代理</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-agent">NOR</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">出运路径</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-route">HFE-PEK-LAX</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">承运人</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-bold" id="detail-carrier">MU</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 移库信息卡片 -->
|
||||
<div class="mt-4 bg-white rounded-xl shadow-sm overflow-hidden border border-gray-200 hidden" id="detail-outbound-card">
|
||||
<div class="bg-[#10B981] px-4 py-2.5">
|
||||
<span class="text-white font-semibold text-[15px]">移库信息</span>
|
||||
</div>
|
||||
<div class="bg-white px-4 py-3 grid grid-cols-2 gap-y-3">
|
||||
<div class="flex flex-col" id="detail-outbound-person-row">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">移库人</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-outbound-person">-</span>
|
||||
</div>
|
||||
<div class="flex flex-col" id="detail-outbound-time-row">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">移库时间</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-outbound-time">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部筛选遮罩 -->
|
||||
<div class="overlay absolute inset-0 z-30" id="filter-overlay" onclick="toggleFilterDrawer(false)"></div>
|
||||
|
||||
<!-- 筛选弹窗 -->
|
||||
<div class="absolute bottom-0 w-full bg-white rounded-t-3xl z-50 p-6 shadow-2xl overflow-hidden" id="filter-panel">
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<h2 class="text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="toggleFilterDrawer(false)">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">承运人</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-800">
|
||||
<span class="text-sm font-medium">MU</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">运单类型</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择运单类型</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">特码</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择特码</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 pt-2">
|
||||
<button class="flex-1 bg-white border border-gray-300 text-gray-600 py-3.5 rounded-xl font-bold btn-active" onclick="alert('已重置')">
|
||||
重置
|
||||
</button>
|
||||
<button class="flex-1 bg-[#2563EB] text-white py-3.5 rounded-xl font-bold shadow-lg shadow-blue-500/20 btn-active" onclick="toggleFilterDrawer(false)">
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 页面切换
|
||||
function switchPage(pageId) {
|
||||
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
||||
document.getElementById(pageId).classList.add('active');
|
||||
if(pageId === 'page-detail'){
|
||||
document.querySelector('#page-detail .no-scrollbar').scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Tab切换
|
||||
function switchTab(type) {
|
||||
const tabPending = document.getElementById('tab-pending');
|
||||
const tabShipped = document.getElementById('tab-shipped');
|
||||
const listPending = document.getElementById('list-pending');
|
||||
const listShipped = document.getElementById('list-shipped');
|
||||
const bottomToolbar = document.getElementById('bottom-toolbar');
|
||||
|
||||
if (type === 'pending') {
|
||||
tabPending.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabPending.classList.remove('text-gray-400', 'border-transparent');
|
||||
tabShipped.classList.add('text-gray-400', 'border-transparent');
|
||||
tabShipped.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
|
||||
listPending.classList.remove('hidden');
|
||||
listShipped.classList.add('hidden');
|
||||
bottomToolbar.style.display = 'flex';
|
||||
} else {
|
||||
tabShipped.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabShipped.classList.remove('text-gray-400', 'border-transparent');
|
||||
tabPending.classList.add('text-gray-400', 'border-transparent');
|
||||
tabPending.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
|
||||
listShipped.classList.remove('hidden');
|
||||
listPending.classList.add('hidden');
|
||||
bottomToolbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 全选
|
||||
function toggleSelectAll(checked) {
|
||||
const checkboxes = document.querySelectorAll('.item-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = checked;
|
||||
});
|
||||
updateSelectionCount();
|
||||
}
|
||||
|
||||
// 单选
|
||||
function handleCheckboxClick(checkbox, event) {
|
||||
event.stopPropagation();
|
||||
updateSelectionCount();
|
||||
const allBox = document.getElementById('select-all-cb');
|
||||
const all = Array.from(document.querySelectorAll('.item-checkbox')).every(i => i.checked);
|
||||
allBox.checked = all;
|
||||
}
|
||||
|
||||
// 更新选中数量
|
||||
function updateSelectionCount() {
|
||||
const cnt = document.querySelectorAll('.item-checkbox:checked').length;
|
||||
document.getElementById('selected-count').textContent = cnt;
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
function showDetail(data) {
|
||||
if (!data) return;
|
||||
const setText = (id, val) => {
|
||||
const el = document.getElementById(id);
|
||||
if(el) el.textContent = val || '-';
|
||||
};
|
||||
setText('detail-awb', data.id);
|
||||
setText('detail-goods-name', data.goodsName);
|
||||
setText('detail-count', data.pieces);
|
||||
setText('detail-weight', data.weight + (String(data.weight).includes('kg') ? '' : ' kg'));
|
||||
setText('detail-code', data.code);
|
||||
setText('detail-agent', data.agent);
|
||||
setText('detail-carrier', data.carrier);
|
||||
setText('detail-route', data.route || 'HFE-PEK-LAX');
|
||||
setText('detail-location', data.location);
|
||||
|
||||
const outboundCard = document.getElementById('detail-outbound-card');
|
||||
if(data.status === '待移库'){
|
||||
outboundCard.classList.add('hidden');
|
||||
}else{
|
||||
outboundCard.classList.remove('hidden');
|
||||
setText('detail-outbound-person', data.outboundPerson);
|
||||
setText('detail-outbound-time', data.outboundTime);
|
||||
}
|
||||
switchPage('page-detail');
|
||||
}
|
||||
|
||||
// 筛选抽屉
|
||||
function toggleFilterDrawer(show) {
|
||||
const panel = document.getElementById('filter-panel');
|
||||
const overlay = document.getElementById('filter-overlay');
|
||||
if(show){
|
||||
panel.classList.add('show');
|
||||
overlay.classList.add('show');
|
||||
}else{
|
||||
panel.classList.remove('show');
|
||||
overlay.classList.remove('show');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
963
documents/5.5寸手持端设计文件/06_出港查询/出港查询.html
Normal file
963
documents/5.5寸手持端设计文件/06_出港查询/出港查询.html
Normal file
@@ -0,0 +1,963 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>出港查询</title>
|
||||
<!-- Tailwind CSS (本地) -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<!-- Iconify 图标 -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'PingFang SC';
|
||||
src: local('PingFang SC');
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #e5e7eb;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 隐藏滚动条但保留功能 */
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.hide-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.tag-occupied { background-color: #dcfce7; color: #22c55e; } /* 已离港 - 绿色 */
|
||||
.tag-available { background-color: #dcfce7; color: #22c55e; } /* 已离港 - 绿色 */
|
||||
.tag-repair { background-color: #fce7e7; color: #ef4444; } /* 离港 - 红色 */
|
||||
|
||||
/* 页面切换动画 */
|
||||
.page {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
background-color: #f3f4f6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.page::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.page-hidden-right { transform: translateX(100%); }
|
||||
.page-hidden-left { transform: translateX(-100%); }
|
||||
|
||||
|
||||
/* 统一的深蓝色调 */
|
||||
.bg-header-blue { background-color: #2563EB; }
|
||||
|
||||
|
||||
/* 标签页样式 */
|
||||
.tab-item {
|
||||
position: relative;
|
||||
padding: 12px 0;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
.tab-item.active {
|
||||
color: #2563eb;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tab-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background-color: #2563eb;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.tab-item.active::after {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
/* ========== 筛选弹窗样式 提升层级+动画稳定 ========== */
|
||||
.filter-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.25s ease, visibility 0.25s ease;
|
||||
}
|
||||
.filter-overlay.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.filter-sheet {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #fff;
|
||||
border-top-left-radius: 20px;
|
||||
border-top-right-radius: 20px;
|
||||
padding: 20px;
|
||||
z-index: 10000;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.25s ease-out;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.filter-sheet.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
/* 筛选输入框统一样式 */
|
||||
.filter-input {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.filter-input:focus {
|
||||
border-color: #2563EB;
|
||||
}
|
||||
|
||||
/* 下拉框箭头浅灰色 + 全浏览器兼容修复 */
|
||||
select.filter-input {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%239ca3af' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10l-5 5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 12px center;
|
||||
background-size: 16px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
/* 清除IE默认下拉箭头 */
|
||||
select.filter-input::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="w-[375px] h-[812px] bg-[#F3F4F6] overflow-hidden flex flex-col relative shadow-2xl rounded-[40px] border-[8px] border-gray-800">
|
||||
<!-- 状态栏 -->
|
||||
<div class="bg-header-blue flex-none z-50">
|
||||
<div class="flex justify-between items-center px-6 pt-3 pb-1 text-[12px] font-medium text-white">
|
||||
<span>09:41</span>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
<iconify-icon icon="material-symbols:signal-cellular-4-bar-rounded"></iconify-icon>
|
||||
<iconify-icon icon="material-symbols:wifi"></iconify-icon>
|
||||
<iconify-icon icon="material-symbols:battery-full-rounded"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选弹窗容器 移到最外层,层级提升 -->
|
||||
<div class="filter-overlay" id="filterOverlay"></div>
|
||||
<div class="filter-sheet" id="filterSheet">
|
||||
<!-- 顶部标题栏 -->
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="closeFilter()">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
<h2 class="text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
</div>
|
||||
|
||||
<!-- 筛选表单区域 -->
|
||||
<div class="space-y-4 mb-7">
|
||||
<!-- 1.航班日期 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班日期起</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择航班日期" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班日期止</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择航班日期" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 2.航班号 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班号</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入航班号" type="text" id="filterFlightNo">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 3.代理 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">代理</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择代理" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 4.特码 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">特码</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择特码" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 5.目的港 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">目的港</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入目的港" type="text" id="filterOrigin">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 6.运单类型 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">运单类型</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择运单类型" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 7.业务类型 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">业务类型</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择业务类型" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 8.品名(中) -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">品名(中)</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入中文品名" type="text" id="filterCnName">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<div class="flex gap-3">
|
||||
<button class="flex-1 h-11 bg-gray-100 text-gray-600 rounded-lg text-sm font-medium" onclick="resetFilter()">重置</button>
|
||||
<button class="flex-1 h-11 bg-[#2563EB] text-white rounded-lg text-sm font-medium" onclick="confirmFilter()">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面主容器 -->
|
||||
<div class="flex-1 relative overflow-hidden">
|
||||
<!-- 列表页 -->
|
||||
<div class="page z-10" id="listPage">
|
||||
<!-- 头部 -->
|
||||
<header class="bg-header-blue pt-2 pb-4 px-4 flex items-center relative flex-none">
|
||||
<button class="flex items-center text-white text-sm absolute left-4">
|
||||
<iconify-icon class="text-xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="mx-auto text-white font-bold text-lg">出港查询</h1>
|
||||
</header>
|
||||
<!-- 搜索栏 筛选按钮已移除边框 -->
|
||||
<div class="p-3 bg-transparent flex items-center gap-3 border-b border-gray-100 flex-none">
|
||||
<div class="flex-1 bg-white rounded-xl flex items-center px-3 py-[10px] gap-2 border border-gray-50">
|
||||
<iconify-icon class="text-[#9ca3af] text-lg" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="bg-transparent text-[14px] w-full outline-none text-gray-700 placeholder-[#9ca3af]" placeholder="搜索运单号" type="text"/>
|
||||
<iconify-icon class="text-[#2563eb] text-lg" icon="mdi:qrcode-scan" onclick="alert('开启扫码功能')"></iconify-icon>
|
||||
</div>
|
||||
<!-- 筛选按钮 无圆形实线边框 -->
|
||||
<button id="filterBtn" class="w-10 h-10 flex items-center justify-center bg-white rounded-full relative z-[99999]">
|
||||
<iconify-icon class="text-[#2563eb] text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 统计标签 -->
|
||||
<div class="flex items-center gap-2 px-4 pb-3">
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#2563EB]">
|
||||
<div class="text-[11px] text-gray-500">总票数</div>
|
||||
<div class="text-[22px] font-bold text-[#2563EB] leading-tight mt-0.5">4</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-orange-500">
|
||||
<div class="text-[11px] text-gray-500">已入库</div>
|
||||
<div class="text-[22px] font-bold text-orange-500 leading-tight mt-0.5">2</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#10B981]">
|
||||
<div class="text-[11px] text-gray-500">已离港</div>
|
||||
<div class="text-[22px] font-bold text-[#10B981] leading-tight mt-0.5">2</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ULD 列表内容 -->
|
||||
<div class="p-3 space-y-3 pb-24 overflow-y-auto hide-scrollbar">
|
||||
<!-- 卡片 1 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363396')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363396</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已离港</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:444</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:555</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:DGR</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 2 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363397')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363397</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已离港</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:120</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:320</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:徽远</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:PER</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 3 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363398')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363398</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已入库</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:268</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:780</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:NOR</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 4 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363399')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363399</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已离港</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:85</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:210</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:徽远</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:DGR</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 5 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363400')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363400</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已离港</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:350</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:430</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:PER</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详情页 -->
|
||||
<div class="page z-20 page-hidden-right !bg-[#f0f2f5]" id="detailPage">
|
||||
<!-- 头部 -->
|
||||
<header class="bg-header-blue pt-2 pb-4 px-4 flex items-center relative flex-none">
|
||||
<button class="flex items-center text-white text-sm absolute left-4 active:opacity-70" onclick="goBack()">
|
||||
<iconify-icon class="text-xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="mx-auto text-white font-bold text-lg">出港详情</h1>
|
||||
<div class="absolute right-4 flex items-center">
|
||||
<iconify-icon class="text-red-500 text-2xl" icon="mdi:file-document-edit-outline"></iconify-icon>
|
||||
</div>
|
||||
</header>
|
||||
<!-- 标签页 -->
|
||||
<div class="bg-white px-4 border-b border-gray-200 flex-none">
|
||||
<div class="flex justify-around">
|
||||
<div class="tab-item active cursor-pointer" onclick="switchTab(0)">运单信息</div>
|
||||
<div class="tab-item cursor-pointer" onclick="switchTab(1)">仓库信息</div>
|
||||
<div class="tab-item cursor-pointer" onclick="switchTab(2)">库位信息</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 详情内容区 -->
|
||||
<div class="flex-1 overflow-y-auto hide-scrollbar">
|
||||
<!-- 运单信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3" id="tab-content-0">
|
||||
<!-- 卡片1:基本信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800" id="det-uldTitle">基本信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-2 gap-2">
|
||||
<!-- Row 1 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-billNo"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">代理人</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-agent"></span>
|
||||
</div>
|
||||
<!-- Row 2 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">特码</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-code"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">承运人</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-carrier"></span>
|
||||
</div>
|
||||
<!-- Row 3 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">始发站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-route"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">中转站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-outboundTime"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">目的站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-dest"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">UN编号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-unNo"></span>
|
||||
</div>
|
||||
<!-- Row 5 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center" id="det-lockStatus-container">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5" id="det-lockStatus-label">锁定状态</span>
|
||||
<div id="det-lockStatus-wrapper">
|
||||
<span class="text-xs font-semibold" id="det-lockStatus"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">包装类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-packType"></span>
|
||||
</div>
|
||||
<!-- Row 6 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">业务类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-type"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库时间</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-outboundTime"></span>
|
||||
</div>
|
||||
<!-- Row 4 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-billType"></span>
|
||||
</div>
|
||||
<!-- Row 7 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">品名(中)</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-itemNameCn"></span>
|
||||
</div>
|
||||
<!-- Row 8 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">品名(英)</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-itemNameEn"></span>
|
||||
</div>
|
||||
<!-- Row 9 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">备注</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-remark"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片2:件重信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800">件重信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-3 gap-2">
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单件数</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-pieces"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-weight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">计费重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-calcWeight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库件数</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-inPieces"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-inWeight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center opacity-0">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">—</div>
|
||||
<div class="text-xs text-gray-800 font-semibold">—</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片3:回执信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800">回执信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-3 gap-2">
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">预配舱单</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-originalManifest"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运抵报告</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-tallyReport"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center" id="det-customsRelease-container">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">海关放行</div>
|
||||
<div id="det-customsRelease-wrapper">
|
||||
<div class="text-xs font-semibold" id="det-customsRelease"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">装载舱单</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-loadManifest"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">理货报告</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-cargoReport"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center opacity-0">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">—</div>
|
||||
<div class="text-xs text-gray-800 font-semibold">—</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 仓库信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3 hidden" id="tab-content-1"></div>
|
||||
<!-- 库位信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3 hidden" id="tab-content-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const uldData = {
|
||||
'78133363396': {
|
||||
billNo: '78133363396', agent: '中外运', code: 'DGR',
|
||||
pieces: '444', weight: '555.0', type: '普通货物运输',
|
||||
inPieces: '444', inWeight: '555.0', calcWeight: '555.0',
|
||||
carrier: 'CA', route: 'HFE', packType: '纸箱',
|
||||
billType: '国际出港(国际直航)', unNo: '—', dest: 'LAX',
|
||||
itemNameCn: '电子元器件', itemNameEn: 'Electronic Components',
|
||||
lockStatus: '未锁定', outboundTime: '—', remark: '需冷藏存储',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '1', pieces: 200, weight: 300.0, flightDate: '2026-07-08', flightNo: 'CA1234',route: 'HFE',dest: 'LAX', time: '2026-07-08 14:30:00' },
|
||||
{ batch: '2', pieces: 244, weight: 255.0, flightDate: '2026-07-09', flightNo: 'CA5678',route: 'HFE',dest: 'LAX', time: '2026-07-09 09:10:46' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'A-01-001', inPerson: '张三', inTime: '2026-07-08 14:30', outPerson: '李四', outTime: '2026-07-09 10:00' },
|
||||
{ id: 'B-02-005', inPerson: '王五', inTime: '2026-07-09 09:10', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363397': {
|
||||
billNo: '78133363397', agent: '徽远', code: 'PER',
|
||||
pieces: '120', weight: '320.0', type: '普通货物运输',
|
||||
inPieces: '120', inWeight: '320.0', calcWeight: '320.0',
|
||||
carrier: 'MU', route: 'PVG-NRT', packType: '托盘',
|
||||
billType: '国际出港(国际直航)', unNo: '—', dest: 'PVG',
|
||||
itemNameCn: '服装', itemNameEn: 'Garments',
|
||||
lockStatus: '未锁定', outboundTime: '—', remark: '易碎品,轻拿轻放',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '3', pieces: 60, weight: 150.0, flightDate: '2026-07-07', flightNo: 'MU123',route: 'HFE',dest: 'LAX', time: '2026-07-07 16:20:00' },
|
||||
{ batch: '4', pieces: 60, weight: 170.0, flightDate: '2026-07-08', flightNo: 'MU456',route: 'HFE',dest: 'LAX', time: '2026-07-08 11:00:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'A-03-012', inPerson: '赵六', inTime: '2026-07-07 16:20', outPerson: '钱七', outTime: '2026-07-08 14:00' },
|
||||
{ id: 'C-01-008', inPerson: '孙八', inTime: '2026-07-08 11:00', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363398': {
|
||||
billNo: '78133363398', agent: '中外运', code: 'NOR',
|
||||
pieces: '268', weight: '780.0', type: '普通货物运输',
|
||||
inPieces: '268', inWeight: '780.0', calcWeight: '780.0',
|
||||
carrier: 'CZ', route: 'CAN-AMS', packType: '托盘',
|
||||
billType: '国际出港(国际直航)', unNo: '—', dest: 'CAN',
|
||||
itemNameCn: '机械设备', itemNameEn: 'Machinery Equipment',
|
||||
lockStatus: '锁定', outboundTime: '2026-07-09 08:00', remark: '需叉车装卸',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '5', pieces: 150, weight: 420.0, flightDate: '2026-07-08', flightNo: 'CZ789',route: 'HFE',dest: 'LAX', time: '2026-07-08 22:15:00' },
|
||||
{ batch: '6', pieces: 118, weight: 360.0, flightDate: '2026-07-09', flightNo: 'CZ012',route: 'HFE',dest: 'LAX', time: '2026-07-09 06:30:00' },
|
||||
{ batch: '7', pieces: 100, weight: 200.0, flightDate: '2026-07-09', flightNo: 'CZ345',route: 'HFE',dest: 'LAX', time: '2026-07-09 13:00:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'B-01-003', inPerson: '张三', inTime: '2026-07-08 22:15', outPerson: '李四', outTime: '2026-07-09 06:00' },
|
||||
{ id: 'A-02-010', inPerson: '王五', inTime: '2026-07-09 06:30', outPerson: '—', outTime: '—' },
|
||||
{ id: 'C-03-007', inPerson: '赵六', inTime: '2026-07-09 13:00', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363399': {
|
||||
billNo: '78133363399', agent: '徽远', code: 'DGR',
|
||||
pieces: '85', weight: '210.0', type: '危险品运输',
|
||||
inPieces: '85', inWeight: '210.0', calcWeight: '210.0',
|
||||
carrier: '3U', route: 'CTU-SIN', packType: '铁箱',
|
||||
billType: '国际出港(国际直航)', unNo: 'UN3480', dest: 'CTU',
|
||||
itemNameCn: '锂电池', itemNameEn: 'Lithium Battery',
|
||||
lockStatus: '锁定', outboundTime: '—', remark: '危险品,单独存放',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '8', pieces: 45, weight: 110.0, flightDate: '2026-07-08', flightNo: '3U678',route: 'HFE',dest: 'LAX', time: '2026-07-08 18:00:00' },
|
||||
{ batch: '9', pieces: 40, weight: 100.0, flightDate: '2026-07-09', flightNo: '3U901',route: 'HFE',dest: 'LAX', time: '2026-07-09 07:45:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'D-01-002', inPerson: '钱七', inTime: '2026-07-08 18:00', outPerson: '孙八', outTime: '2026-07-09 08:00' },
|
||||
{ id: 'A-04-015', inPerson: '张三', inTime: '2026-07-09 07:45', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363400': {
|
||||
billNo: '78133363400', agent: '中外运', code: 'PER',
|
||||
pieces: '350', weight: '430.0', type: '普通货物运输',
|
||||
inPieces: '350', inWeight: '430.0', calcWeight: '430.0',
|
||||
carrier: 'HU', route: 'HKG-HFE', packType: '纸箱',
|
||||
billType: '国际出港(国际直航)', unNo: '—', dest: 'HFE',
|
||||
itemNameCn: '食品', itemNameEn: 'Food Products',
|
||||
lockStatus: '未锁定', outboundTime: '2026-07-09 11:30', remark: '保质期至2026-12',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '10', pieces: 180, weight: 220.0, flightDate: '2026-07-07', flightNo: 'HU234',route: 'HFE',dest: 'LAX', time: '2026-07-07 20:00:00' },
|
||||
{ batch: '11', pieces: 170, weight: 210.0, flightDate: '2026-07-09', flightNo: 'HU567',route: 'HFE',dest: 'LAX', time: '2026-07-09 10:30:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'B-02-011', inPerson: '李四', inTime: '2026-07-07 20:00', outPerson: '王五', outTime: '2026-07-08 16:00' },
|
||||
{ id: 'C-01-009', inPerson: '赵六', inTime: '2026-07-09 10:30', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// 核心修复:等待DOM全部渲染完成后再绑定点击事件,避免获取元素为null
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
const filterBtn = document.getElementById('filterBtn');
|
||||
|
||||
// 点击遮罩空白关闭,添加self防止弹窗内部点击穿透关闭
|
||||
filterOverlay.addEventListener('click', function(e) {
|
||||
if (e.target === filterOverlay) {
|
||||
closeFilter();
|
||||
}
|
||||
});
|
||||
// 筛选按钮点击打开弹窗
|
||||
filterBtn.addEventListener('click', openFilter);
|
||||
});
|
||||
|
||||
// 打开筛选弹窗
|
||||
function openFilter() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
filterOverlay.classList.add('show');
|
||||
setTimeout(() => filterSheet.classList.add('show'), 10);
|
||||
}
|
||||
|
||||
// 关闭筛选弹窗
|
||||
function closeFilter() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
filterSheet.classList.remove('show');
|
||||
setTimeout(() => filterOverlay.classList.remove('show'), 250);
|
||||
}
|
||||
|
||||
// 重置所有筛选输入框
|
||||
function resetFilter() {
|
||||
document.querySelectorAll('.filter-input').forEach(input => {
|
||||
input.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
// 确定筛选(模拟查询,可对接后端筛选逻辑)
|
||||
function confirmFilter() {
|
||||
const params = {
|
||||
flightDate: document.getElementById('filterFlightDate').value,
|
||||
flightNo: document.getElementById('filterFlightNo').value,
|
||||
agent: document.getElementById('filterAgent').value,
|
||||
code: document.getElementById('filterCode').value,
|
||||
origin: document.getElementById('filterOrigin').value,
|
||||
billType: document.getElementById('filterBillType').value,
|
||||
bizType: document.getElementById('filterBizType').value,
|
||||
cnName: document.getElementById('filterCnName').value
|
||||
};
|
||||
console.log('筛选参数:', params);
|
||||
alert('筛选执行成功,控制台可查看筛选条件');
|
||||
closeFilter();
|
||||
}
|
||||
|
||||
function showDetail(id) {
|
||||
const listPage = document.getElementById('listPage');
|
||||
const detailPage = document.getElementById('detailPage');
|
||||
|
||||
const data = uldData[id];
|
||||
if (data) {
|
||||
document.getElementById('det-uldTitle').innerText = '基本信息';
|
||||
document.getElementById('det-billNo').innerText = data.billNo;
|
||||
document.getElementById('det-agent').innerText = data.agent;
|
||||
document.getElementById('det-code').innerText = data.code;
|
||||
document.getElementById('det-carrier').innerText = data.carrier;
|
||||
document.getElementById('det-route').innerText = data.route;
|
||||
document.getElementById('det-dest').innerText = data.dest;
|
||||
document.getElementById('det-billType').innerText = data.billType;
|
||||
document.getElementById('det-unNo').innerText = data.unNo;
|
||||
|
||||
// 锁定状态渲染
|
||||
const lockStatusEl = document.getElementById('det-lockStatus');
|
||||
if (data.lockStatus === '锁定') {
|
||||
lockStatusEl.innerHTML = '<iconify-icon icon="mdi:lock" class="mr-1"></iconify-icon>锁定';
|
||||
lockStatusEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-red-50 text-red-600 inline-flex items-center';
|
||||
} else {
|
||||
lockStatusEl.innerHTML = '—';
|
||||
lockStatusEl.className = 'text-xs font-semibold text-gray-800';
|
||||
}
|
||||
|
||||
document.getElementById('det-packType').innerText = data.packType;
|
||||
document.getElementById('det-type').innerText = data.type;
|
||||
document.getElementById('det-outboundTime').innerText = data.outboundTime || '—';
|
||||
document.getElementById('det-itemNameCn').innerText = data.itemNameCn || '—';
|
||||
document.getElementById('det-itemNameEn').innerText = data.itemNameEn || '—';
|
||||
document.getElementById('det-remark').innerText = data.remark || '—';
|
||||
|
||||
// 件重信息
|
||||
document.getElementById('det-pieces').innerText = data.pieces;
|
||||
document.getElementById('det-weight').innerText = data.weight;
|
||||
document.getElementById('det-calcWeight').innerText = data.calcWeight;
|
||||
document.getElementById('det-inPieces').innerText = data.inPieces;
|
||||
document.getElementById('det-inWeight').innerText = data.inWeight;
|
||||
|
||||
// 海关放行标签
|
||||
const customsReleaseEl = document.getElementById('det-customsRelease');
|
||||
if (data.customsRelease === '11') {
|
||||
customsReleaseEl.innerText = '11';
|
||||
customsReleaseEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-green-50 text-green-600 inline-block';
|
||||
} else {
|
||||
customsReleaseEl.innerText = data.customsRelease;
|
||||
customsReleaseEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-gray-100 text-gray-600 inline-block';
|
||||
}
|
||||
|
||||
// 渲染仓库批次列表
|
||||
const warehouseContainer = document.getElementById('tab-content-1');
|
||||
warehouseContainer.innerHTML = '';
|
||||
data.warehouse.forEach(item => {
|
||||
warehouseContainer.innerHTML += `
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="flex justify-between items-center pt-3 pb-1.5 px-3 mb-2 border-b border-gray-100">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block"></span>
|
||||
<span class="text-xs font-semibold text-gray-900">批次号: ${item.batch}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-1.5 px-3 pb-2">
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">件数</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.pieces}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">重量</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${parseFloat(item.weight).toFixed(1)} kg</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">航班日期</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.flightDate}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">航班号</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.flightNo}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">始发站</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.route}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">目的站</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.dest}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
// 渲染库位信息
|
||||
const locationContainer = document.getElementById('tab-content-2');
|
||||
locationContainer.innerHTML = '';
|
||||
data.locations.forEach(loc => {
|
||||
locationContainer.innerHTML += `
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="flex justify-between items-center pt-3 pb-1.5 px-3 mb-2 border-b border-gray-100">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block"></span>
|
||||
<span class="text-xs font-bold text-gray-900">${loc.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-1.5 px-3 pb-2">
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库人</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${loc.inPerson}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库时间</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${loc.inTime}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">出库人</div>
|
||||
<div class="text-xs ${loc.outPerson === '—' ? 'text-gray-500 text-[14px]' : 'text-gray-800'} font-semibold">${loc.outPerson}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">出库时间</div>
|
||||
<div class="text-xs ${loc.outTime === '—' ? 'text-gray-500 text-[14px]' : 'text-gray-800'} font-semibold">${loc.outTime}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
}
|
||||
switchTab(0);
|
||||
listPage.classList.add('page-hidden-left');
|
||||
detailPage.classList.remove('page-hidden-right');
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
const listPage = document.getElementById('listPage');
|
||||
const detailPage = document.getElementById('detailPage');
|
||||
listPage.classList.remove('page-hidden-left');
|
||||
detailPage.classList.add('page-hidden-right');
|
||||
}
|
||||
|
||||
function switchTab(index) {
|
||||
const tabs = document.querySelectorAll('.tab-item');
|
||||
const contents = document.querySelectorAll('.tab-pane');
|
||||
tabs.forEach((tab, i) => {
|
||||
if (i === index) {
|
||||
tab.classList.add('active');
|
||||
contents[i].classList.remove('hidden');
|
||||
} else {
|
||||
tab.classList.remove('active');
|
||||
contents[i].classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 搜索框聚焦监听
|
||||
document.querySelector('input[placeholder="搜索ULD编号"]').addEventListener('focus', () => {
|
||||
console.log('搜索框已激活');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
139
documents/5.5寸手持端设计文件/06_出港查询/运单追踪.html
Normal file
139
documents/5.5寸手持端设计文件/06_出港查询/运单追踪.html
Normal file
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>国际进港查询 - 物流跟踪</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
src: local('sans-serif');
|
||||
}
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.no-scrollbar::-webkit-scrollbar { display: none; }
|
||||
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
|
||||
/* 模拟手机容器阴影 */
|
||||
.phone-shell {
|
||||
box-shadow: 0 50px 100px -20px rgba(0,0,0,0.25), 0 30px 60px -30px rgba(0,0,0,0.3);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-200 flex justify-center items-center min-h-screen p-4">
|
||||
<!-- 模拟手机屏幕容器 -->
|
||||
<div class="phone-shell w-[375px] h-[812px] bg-[#F5F5F7] overflow-hidden flex flex-col relative rounded-[48px] border-[10px] border-gray-900">
|
||||
<!-- 顶部蓝色区域 (包含状态栏和导航栏) -->
|
||||
<div class="bg-[#1A73E8] flex-none">
|
||||
<!-- 系统状态栏模拟 -->
|
||||
<div class="h-11 w-full flex justify-between items-center px-8 text-white">
|
||||
<span class="text-[15px] font-bold">09:41</span>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
<iconify-icon class="text-sm" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-base" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-xl" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<div class="h-[44px] px-4 flex items-center justify-between">
|
||||
<button class="flex items-center text-white space-x-0.5">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span class="text-base">返回</span>
|
||||
</button>
|
||||
<h1 class="text-lg font-bold text-white absolute left-1/2 -translate-x-1/2">运单追踪</h1>
|
||||
<div class="w-10"></div> <!-- 占位保持平衡 -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- 可滚动内容区 -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar px-5 pt-6 pb-12">
|
||||
<!-- 流转状态卡片 -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-5">
|
||||
<!-- 运单号 Header -->
|
||||
<div class="flex items-center justify-between pt-2 pb-5 mb-8 border-b border-gray-100">
|
||||
<div class="text-xl font-black text-gray-900 leading-none tracking-tight">
|
||||
78133358743
|
||||
</div>
|
||||
<span class="text-[11px] font-medium text-blue-600 bg-blue-50 px-2.5 py-1 rounded-full border border-blue-100">国际进港</span>
|
||||
</div>
|
||||
<!-- 流转状态标题 -->
|
||||
<div class="mb-6">
|
||||
<h2 class="text-base font-semibold text-gray-700 flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-gray-700 inline-block" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
流转状态
|
||||
</h2>
|
||||
</div>
|
||||
<div class="relative pl-7 pb-2">
|
||||
<div class="absolute left-[9px] top-2 bottom-4 w-[2px] bg-gray-100"></div>
|
||||
<!-- 状态 1: 当前 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[27px] top-1 w-[20px] h-[20px] rounded-full bg-green-600 border-4 border-green-100 flex items-center justify-center ring-1 ring-green-600/20">
|
||||
<div class="w-2 h-2 rounded-full bg-white"></div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-green-700">原始舱单</span>
|
||||
<span class="text-green-600 text-xs font-normal bg-green-50 px-2 py-0.5 rounded-full">放行状态:01</span>
|
||||
</div>
|
||||
<div class="text-gray-400 text-xs font-normal flex items-center gap-1.5">
|
||||
<span>2026-07-10 11:03:38</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 2 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">分拣理货</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 3 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">理货申报</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 4 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">海关放行</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 5 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">柜台办结</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 6 -->
|
||||
<div class="relative mb-2">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">提取出库</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 模块 4: 操作详情卡片 -> 已删除 -->
|
||||
<!-- 底部占位 -->
|
||||
<div class="text-center py-6">
|
||||
<p class="text-[10px] text-gray-300 uppercase tracking-[0.2em] font-bold">查询信息已触底</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部 Home Indicator 模拟 -->
|
||||
<div class="h-8 w-full flex justify-center items-center flex-none bg-white/60 backdrop-blur-md">
|
||||
<div class="w-32 h-1.5 bg-black rounded-full opacity-20"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>ml>
|
||||
932
documents/5.5寸手持端设计文件/07_出港仓库/出港仓库.html
Normal file
932
documents/5.5寸手持端设计文件/07_出港仓库/出港仓库.html
Normal file
@@ -0,0 +1,932 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>出港仓库</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'PingFang SC';
|
||||
src: local('PingFang SC');
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #e5e7eb;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 隐藏滚动条但保留功能 */
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.hide-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.tag-occupied { background-color: #dcfce7; color: #22c55e; } /* 已离港 - 绿色 */
|
||||
.tag-available { background-color: #dcfce7; color: #22c55e; } /* 已离港 - 绿色 */
|
||||
.tag-repair { background-color: #fce7e7; color: #ef4444; } /* 离港 - 红色 */
|
||||
|
||||
/* 页面切换动画 */
|
||||
.page {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
background-color: #f3f4f6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.page::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.page-hidden-right { transform: translateX(100%); }
|
||||
.page-hidden-left { transform: translateX(-100%); }
|
||||
|
||||
|
||||
/* 统一的深蓝色调 */
|
||||
.bg-header-blue { background-color: #2563EB; }
|
||||
|
||||
|
||||
/* 标签页样式 */
|
||||
.tab-item {
|
||||
position: relative;
|
||||
padding: 12px 0;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
.tab-item.active {
|
||||
color: #2563eb;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tab-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background-color: #2563eb;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.tab-item.active::after {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
/* ========== 筛选弹窗样式 提升层级+动画稳定 ========== */
|
||||
.filter-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.25s ease, visibility 0.25s ease;
|
||||
}
|
||||
.filter-overlay.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.filter-sheet {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #fff;
|
||||
border-top-left-radius: 20px;
|
||||
border-top-right-radius: 20px;
|
||||
padding: 20px;
|
||||
z-index: 10000;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.25s ease-out;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.filter-sheet.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
/* 筛选输入框统一样式 */
|
||||
.filter-input {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.filter-input:focus {
|
||||
border-color: #2563EB;
|
||||
}
|
||||
|
||||
/* 下拉框箭头浅灰色 + 全浏览器兼容修复 */
|
||||
select.filter-input {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%239ca3af' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10l-5 5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 12px center;
|
||||
background-size: 16px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
/* 清除IE默认下拉箭头 */
|
||||
select.filter-input::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes scan {
|
||||
0% { top: 0; }
|
||||
100% { top: 100%; }
|
||||
}
|
||||
.scan-line {
|
||||
animation: scan 2s linear infinite;
|
||||
}
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.bottom-drawer {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="w-[375px] h-[812px] bg-[#F3F4F6] overflow-hidden flex flex-col relative shadow-2xl rounded-[40px] border-[8px] border-gray-800">
|
||||
<!-- 状态栏 -->
|
||||
<div class="h-6 w-full bg-[#2563EB] flex justify-between items-center px-6 text-white text-xs pt-1">
|
||||
<span>09:41</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<iconify-icon icon="material-symbols:signal-cellular-4-bar-rounded"></iconify-icon>
|
||||
<iconify-icon icon="material-symbols:wifi"></iconify-icon>
|
||||
<iconify-icon icon="material-symbols:battery-full-rounded"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 筛选弹窗容器 移到最外层,层级提升 -->
|
||||
<div class="filter-overlay" id="filterOverlay"></div>
|
||||
<div class="filter-sheet" id="filterSheet">
|
||||
<!-- 顶部标题栏 -->
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="closeFilter()">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
<h2 class="text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
</div>
|
||||
<!-- 筛选表单区域 -->
|
||||
<div class="space-y-4 mb-7">
|
||||
<!-- 1.航班日期 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班日期</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择航班日期" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 2.航班号 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班号</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入航班号" type="text" id="filterFlightNo">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 3.代理 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">代理</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择代理" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 4.特码 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">特码</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择特码" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 5.目的站 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">目的站</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入目的站" type="text" id="filterOrigin">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 6.运单类型 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">运单类型</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择运单类型" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 7.业务类型 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">业务类型</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择业务类型" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 8.品名(中) -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">品名(中)</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入中文品名" type="text" id="filterCnName">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部操作按钮 -->
|
||||
<div class="flex gap-3">
|
||||
<button class="flex-1 h-11 bg-gray-100 text-gray-600 rounded-lg text-sm font-medium" onclick="resetFilter()">重置</button>
|
||||
<button class="flex-1 h-11 bg-[#2563EB] text-white rounded-lg text-sm font-medium" onclick="confirmFilter()">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面主容器 -->
|
||||
<div class="flex-1 relative overflow-hidden">
|
||||
<!-- 列表首页:出港仓库卡片页面 -->
|
||||
<div class="page z-10" id="listPage">
|
||||
<!-- 顶部导航栏 -->
|
||||
<header class="bg-[#2563EB] h-14 flex items-center justify-between px-4 flex-none">
|
||||
<button class="flex items-center text-white text-sm" onclick="showToast('返回上一级')">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="text-white font-bold text-lg">出港仓库</h1>
|
||||
<div class="w-12"></div>
|
||||
</header>
|
||||
<!-- 搜索区 -->
|
||||
<section class="bg-[#F3F4F6] p-3 flex-none">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex-1 relative">
|
||||
<iconify-icon class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-lg" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="w-full bg-white rounded-full pl-10 pr-10 py-2.5 text-sm shadow-sm border border-gray-200 outline-none" onkeypress="if(event.keyCode==13) showToast('执行搜索')" placeholder="搜索运单号" type="text"/>
|
||||
<button class="absolute right-2 top-1/2 -translate-y-1/2 p-1 text-[#2563EB]" onclick="toggleScanOverlay(true)">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button id="filterBtn" class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="openFilter()">
|
||||
<iconify-icon class="text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<!-- 统计栏 -->
|
||||
<section class="px-3 pb-3 flex justify-between gap-2 flex-none">
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#2563EB]">
|
||||
<div class="text-[11px] text-gray-500">总票数</div>
|
||||
<div class="text-lg font-bold text-[#2563EB]">8</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-orange-500">
|
||||
<div class="text-[11px] text-gray-500">未清仓</div>
|
||||
<div class="text-lg font-bold text-orange-500">4</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#10B981]">
|
||||
<div class="text-[11px] text-gray-500">已清仓</div>
|
||||
<div class="text-lg font-bold text-[#10B981]">4</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- 卡片列表容器 -->
|
||||
<main class="flex-1 overflow-y-auto px-3 pb-4 custom-scrollbar" id="cardListContainer">
|
||||
</main>
|
||||
<!-- 扫码层 -->
|
||||
<div class="absolute inset-0 z-50 bg-black hidden flex flex-col" id="scanOverlay">
|
||||
<div class="p-6 flex justify-between items-center text-white">
|
||||
<button onclick="toggleScanOverlay(false)">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
<span class="text-sm font-medium">扫描运单条码</span>
|
||||
<button><iconify-icon class="text-2xl" icon="mdi:flashlight"></iconify-icon></button>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col items-center justify-center p-12">
|
||||
<div class="w-64 h-64 border-2 border-[#2563EB] relative overflow-hidden rounded-lg">
|
||||
<div class="absolute top-0 left-0 w-full h-0.5 bg-[#2563EB] shadow-[0_0_10px_#2563EB] scan-line"></div>
|
||||
<div class="absolute top-0 left-0 w-6 h-6 border-t-4 border-l-4 border-[#2563EB]"></div>
|
||||
<div class="absolute top-0 right-0 w-6 h-6 border-t-4 border-r-4 border-[#2563EB]"></div>
|
||||
<div class="absolute bottom-0 left-0 w-6 h-6 border-b-4 border-l-4 border-[#2563EB]"></div>
|
||||
<div class="absolute bottom-0 right-0 w-6 h-6 border-b-4 border-r-4 border-[#2563EB]"></div>
|
||||
</div>
|
||||
<p class="text-white text-xs mt-6 opacity-60 text-center px-4">将条码/二维码放入框内,即可自动扫描</p>
|
||||
</div>
|
||||
<div class="h-32 bg-black flex justify-center items-center gap-12 text-white">
|
||||
<div class="flex flex-col items-center opacity-60">
|
||||
<iconify-icon class="text-3xl" icon="mdi:image-outline"></iconify-icon>
|
||||
<span class="text-[10px] mt-1">相册</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<iconify-icon class="text-3xl" icon="mdi:keyboard-outline"></iconify-icon>
|
||||
<span class="text-[10px] mt-1">手动输入</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toast -->
|
||||
<div class="absolute bottom-20 left-1/2 -translate-x-1/2 bg-black bg-opacity-80 text-white text-xs px-4 py-2 rounded-full hidden z-[60] whitespace-nowrap" id="toast">提示内容</div>
|
||||
</div>
|
||||
|
||||
<!-- 出港货物页面(右滑弹出) -->
|
||||
<div class="page z-20 page-hidden-right !bg-[#f0f2f5]" id="detailPage">
|
||||
<!-- 头部 -->
|
||||
<header class="bg-header-blue pt-2 pb-4 px-4 flex items-center relative flex-none">
|
||||
<button class="flex items-center text-white text-sm absolute left-4 active:opacity-70" onclick="goBack()">
|
||||
<iconify-icon class="text-xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="mx-auto text-white font-bold text-lg">出港货物</h1>
|
||||
<div class="absolute right-4 flex items-center">
|
||||
<iconify-icon class="text-red-500 text-2xl" icon="mdi:file-document-edit-outline"></iconify-icon>
|
||||
</div>
|
||||
</header>
|
||||
<!-- 标签页 -->
|
||||
<div class="bg-white px-4 border-b border-gray-200 flex-none">
|
||||
<div class="flex justify-around">
|
||||
<div class="tab-item active cursor-pointer" onclick="switchTab(0)">运单信息</div>
|
||||
<div class="tab-item cursor-pointer" onclick="switchTab(1)">仓库信息</div>
|
||||
<div class="tab-item cursor-pointer" onclick="switchTab(2)">库位信息</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 详情内容区 -->
|
||||
<div class="flex-1 overflow-y-auto hide-scrollbar">
|
||||
<!-- 运单信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3" id="tab-content-0">
|
||||
<!-- 卡片1:基本信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800" id="det-uldTitle">基本信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-2 gap-2">
|
||||
<!-- Row 1 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-billNo"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">代理人</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-agent"></span>
|
||||
</div>
|
||||
<!-- Row 2 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">特码</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-code"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">承运人</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-carrier"></span>
|
||||
</div>
|
||||
<!-- Row 3 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">始发站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-route"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">中转站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-outboundTime"></span>
|
||||
</div>
|
||||
<!-- Row 4 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">目的站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-dest"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">UN编号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-unNo"></span>
|
||||
</div>
|
||||
<!-- Row 5 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center" id="det-lockStatus-container">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5" id="det-lockStatus-label">锁定状态</span>
|
||||
<div id="det-lockStatus-wrapper">
|
||||
<span class="text-xs font-semibold" id="det-lockStatus"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">包装类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-packType"></span>
|
||||
</div>
|
||||
<!-- Row 6 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">业务类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-type"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库时间</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-ciTime"></span>
|
||||
</div>
|
||||
<!-- Row 7 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-billType"></span>
|
||||
</div>
|
||||
<!-- Row 8 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">品名(中)</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-itemNameCn"></span>
|
||||
</div>
|
||||
<!-- Row 9 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">品名(英)</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-itemNameEn"></span>
|
||||
</div>
|
||||
<!-- Row 10 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">备注</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-remark"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片2:件重信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800">件重信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-3 gap-2">
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单件数</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-pieces"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-weight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">计费重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-calcWeight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库件数</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-inPieces"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-inWeight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center opacity-0">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">—</div>
|
||||
<div class="text-xs text-gray-800 font-semibold">—</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片3:回执信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800">回执信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-3 gap-2">
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">预配舱单</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-originalManifest"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运抵报告</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-arriReport"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center" id="det-customsRelease-container">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">海关放行</div>
|
||||
<div id="det-customsRelease-wrapper">
|
||||
<div class="text-xs font-semibold" id="det-customsRelease"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">装载舱单</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-mftReport"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">理货报告</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-tallyReport"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 仓库信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3 hidden" id="tab-content-1"></div>
|
||||
<!-- 库位信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3 hidden" id="tab-content-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 运单详情数据源(用于出港货物渲染)
|
||||
const uldData = {
|
||||
'78133363396': {
|
||||
billNo: '78133363396', agent: '中外运', code: 'DGR',
|
||||
pieces: '444', weight: '555.0', type: '普通货物运输',
|
||||
inPieces: '444', inWeight: '555.0', calcWeight: '555.0',
|
||||
carrier: 'CA', route: 'HFE', packType: '纸箱',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'LAX',
|
||||
itemNameCn: '电子元器件', itemNameEn: 'Electronic Components',
|
||||
lockStatus: '未锁定', outboundTime: '—', remark: '需冷藏存储',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',arriReport: '01',mftReport: '01',
|
||||
warehouse: [
|
||||
{ batch: '1', pieces: 200, weight: 300.0, flightDate: '2026-07-08', flightNo: 'CA1234',route: 'HFE',dest: 'LAX', time: '2026-07-08 14:30:00' },
|
||||
{ batch: '2', pieces: 244, weight: 255.0, flightDate: '2026-07-09', flightNo: 'CA5678',route: 'HFE',dest: 'LAX', time: '2026-07-09 09:10:46' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'A-01-001', inPerson: '张三', inTime: '2026-07-08 14:30', outPerson: '李四', outTime: '2026-07-09 10:00' },
|
||||
{ id: 'B-02-005', inPerson: '王五', inTime: '2026-07-09 09:10', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363397': {
|
||||
billNo: '78133363397', agent: '徽远', code: 'PER',
|
||||
pieces: '120', weight: '320.0', type: '普通货物运输',
|
||||
inPieces: '120', inWeight: '320.0', calcWeight: '320.0',
|
||||
carrier: 'MU', route: 'PVG-NRT', packType: '托盘',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'PVG',
|
||||
itemNameCn: '服装', itemNameEn: 'Garments',
|
||||
lockStatus: '未锁定', outboundTime: '—', remark: '易碎品,轻拿轻放',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',arriReport: '01',mftReport: '01',
|
||||
warehouse: [
|
||||
{ batch: '3', pieces: 60, weight: 150.0, flightDate: '2026-07-07', flightNo: 'MU123', time: '2026-07-07 16:20:00' },
|
||||
{ batch: '4', pieces: 60, weight: 170.0, flightDate: '2026-07-08', flightNo: 'MU456', time: '2026-07-08 11:00:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'A-03-012', inPerson: '赵六', inTime: '2026-07-07 16:20', outPerson: '钱七', outTime: '2026-07-08 14:00' },
|
||||
{ id: 'C-01-008', inPerson: '孙八', inTime: '2026-07-08 11:00', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363398': {
|
||||
billNo: '78133363398', agent: '中外运', code: 'NOR',
|
||||
pieces: '268', weight: '780.0', type: '普通货物运输',
|
||||
inPieces: '268', inWeight: '780.0', calcWeight: '780.0',
|
||||
carrier: 'CZ', route: 'CAN-AMS', packType: '托盘',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'CAN',
|
||||
itemNameCn: '机械设备', itemNameEn: 'Machinery Equipment',
|
||||
lockStatus: '锁定', outboundTime: '2026-07-09 08:00', remark: '需叉车装卸',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',arriReport: '01',mftReport: '01',
|
||||
warehouse: [
|
||||
{ batch: '5', pieces: 150, weight: 420.0, flightDate: '2026-07-08', flightNo: 'CZ789', time: '2026-07-08 22:15:00' },
|
||||
{ batch: '6', pieces: 118, weight: 360.0, flightDate: '2026-07-09', flightNo: 'CZ012', time: '2026-07-09 06:30:00' },
|
||||
{ batch: '7', pieces: 100, weight: 200.0, flightDate: '2026-07-09', flightNo: 'CZ345', time: '2026-07-09 13:00:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'B-01-003', inPerson: '张三', inTime: '2026-07-08 22:15', outPerson: '李四', outTime: '2026-07-09 06:00' },
|
||||
{ id: 'A-02-010', inPerson: '王五', inTime: '2026-07-09 06:30', outPerson: '—', outTime: '—' },
|
||||
{ id: 'C-03-007', inPerson: '赵六', inTime: '2026-07-09 13:00', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363399': {
|
||||
billNo: '78133363399', agent: '徽远', code: 'DGR',
|
||||
pieces: '85', weight: '210.0', type: '危险品运输',
|
||||
inPieces: '85', inWeight: '210.0', calcWeight: '210.0',
|
||||
carrier: '3U', route: 'CTU-SIN', packType: '铁箱',
|
||||
billType: '国际进港(国际直航)', unNo: 'UN3480', dest: 'CTU',
|
||||
itemNameCn: '锂电池', itemNameEn: 'Lithium Battery',
|
||||
lockStatus: '锁定', outboundTime: '—', remark: '危险品,单独存放',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',arriReport: '01',mftReport: '01',
|
||||
warehouse: [
|
||||
{ batch: '8', pieces: 45, weight: 110.0, flightDate: '2026-07-08', flightNo: '3U678', time: '2026-07-08 18:00:00' },
|
||||
{ batch: '9', pieces: 40, weight: 100.0, flightDate: '2026-07-09', flightNo: '3U901', time: '2026-07-09 07:45:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'D-01-002', inPerson: '钱七', inTime: '2026-07-08 18:00', outPerson: '孙八', outTime: '2026-07-09 08:00' },
|
||||
{ id: 'A-04-015', inPerson: '张三', inTime: '2026-07-09 07:45', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363400': {
|
||||
billNo: '78133363400', agent: '中外运', code: 'PER',
|
||||
pieces: '350', weight: '430.0', type: '普通货物运输',
|
||||
inPieces: '350', inWeight: '430.0', calcWeight: '430.0',
|
||||
carrier: 'HU', route: 'HKG-LAX', packType: '纸箱',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'LAX',
|
||||
itemNameCn: '食品', itemNameEn: 'Food Products',
|
||||
lockStatus: '未锁定', outboundTime: '2026-07-09 11:30', remark: '保质期至2026-12',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',arriReport: '01',mftReport: '01',
|
||||
warehouse: [
|
||||
{ batch: '10', pieces: 180, weight: 220.0, flightDate: '2026-07-07', flightNo: 'HU234', time: '2026-07-07 20:00:00' },
|
||||
{ batch: '11', pieces: 170, weight: 210.0, flightDate: '2026-07-09', flightNo: 'HU567', time: '2026-07-09 10:30:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'B-02-011', inPerson: '李四', inTime: '2026-07-07 20:00', outPerson: '王五', outTime: '2026-07-08 16:00' },
|
||||
{ id: 'C-01-009', inPerson: '赵六', inTime: '2026-07-09 10:30', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
}
|
||||
};
|
||||
// 出港仓库卡片列表数据源
|
||||
const data = [
|
||||
{ id: "78133363396", status: "已清仓", flight: "20260702/MU1111", route: "HFE-LAX", pcs: 111, weight: 2300 },
|
||||
{ id: "78133363397", status: "未清仓", flight: "20260702/MU2222", route: "NRT-LAX", pcs: 85, weight: 1560 },
|
||||
{ id: "78133363398", status: "已清仓", flight: "20260702/MU3333", route: "FRA-LAX", pcs: 200, weight: 4200 },
|
||||
{ id: "78133363399", status: "未清仓", flight: "20260702/MU4444", route: "HFE-PVG", pcs: 50, weight: 980 },
|
||||
{ id: "78133363400", status: "未清仓", flight: "20260702/MU5555", route: "CDG-LAX", pcs: 168, weight: 3150 },
|
||||
{ id: "78133363401", status: "已清仓", flight: "20260702/MU6666", route: "SIN-LAX", pcs: 92, weight: 1800 },
|
||||
{ id: "78133363402", status: "未清仓", flight: "20260702/MU7777", route: "HFE-HFE", pcs: 145, weight: 2800 },
|
||||
{ id: "78133363403", status: "已清仓", flight: "20260702/MU8888", route: "HND-LAX", pcs: 73, weight: 1420 }
|
||||
];
|
||||
|
||||
// 渲染仓库卡片列表
|
||||
function renderList() {
|
||||
const container = document.getElementById('cardListContainer');
|
||||
container.innerHTML = data.map((item) => {
|
||||
const isCleared = item.status === '已清仓';
|
||||
const departure = item.route.split('-')[0];
|
||||
const flightNo = item.flight.split('/')[1];
|
||||
return `
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer mb-3" onclick="showDetail('${item.id}')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">${item.id}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
${getStatusTag(item.status)}
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:${item.pcs}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:${item.weight}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span>航班号:${flightNo}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>目的站:${departure}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 gap-2 mt-3">
|
||||
<button onclick="event.stopPropagation(); showToast('执行清仓操作:运单号 ${item.id}')"
|
||||
class="text-xs py-1.5 px-2 rounded-[6px] font-medium h-8 ${isCleared ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-[#EFF6FF] text-[#2563EB]'}" ${isCleared ? 'disabled' : ''}>
|
||||
清仓
|
||||
</button>
|
||||
<button onclick="event.stopPropagation(); showToast('修改库位:运单号 ${item.id}')"
|
||||
class="bg-[#EFF6FF] text-[#2563EB] text-xs py-1.5 px-2 rounded-[6px] font-medium h-8">
|
||||
修改库位
|
||||
</button>
|
||||
<button onclick="event.stopPropagation(); showToast('执行出库:运单号 ${item.id}')"
|
||||
class="bg-[#EFF6FF] text-[#2563EB] text-xs py-1.5 px-2 rounded-[6px] font-medium h-8">
|
||||
出库
|
||||
</button>
|
||||
<button onclick="event.stopPropagation(); showToast('执行入库:运单号 ${item.id}')"
|
||||
class="bg-[#EFF6FF] text-[#2563EB] text-xs py-1.5 px-2 rounded-[6px] font-medium h-8">
|
||||
入库
|
||||
</button>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// 状态标签渲染
|
||||
function getStatusTag(status) {
|
||||
let color = '';
|
||||
switch(status) {
|
||||
case '已清仓': color = 'bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium'; break;
|
||||
case '未清仓': color = 'bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium'; break;
|
||||
}
|
||||
return `<span class="${color}">${status}</span>`;
|
||||
}
|
||||
|
||||
// 跳转至出港货物页面,填充数据
|
||||
function showDetail(id) {
|
||||
const listPage = document.getElementById('listPage');
|
||||
const detailPage = document.getElementById('detailPage');
|
||||
|
||||
const data = uldData[id];
|
||||
if (!data) {
|
||||
showToast("暂无该运单详情数据");
|
||||
return;
|
||||
}
|
||||
// 填充基础信息
|
||||
document.getElementById('det-billNo').innerText = data.billNo;
|
||||
document.getElementById('det-agent').innerText = data.agent;
|
||||
document.getElementById('det-code').innerText = data.code;
|
||||
document.getElementById('det-carrier').innerText = data.carrier;
|
||||
document.getElementById('det-route').innerText = data.route;
|
||||
document.getElementById('det-dest').innerText = data.dest;
|
||||
document.getElementById('det-billType').innerText = data.billType;
|
||||
document.getElementById('det-unNo').innerText = data.unNo;
|
||||
document.getElementById('det-ciTime').innerText = data.ciTime;
|
||||
// 锁定状态样式
|
||||
const lockStatusEl = document.getElementById('det-lockStatus');
|
||||
if (data.lockStatus === '锁定') {
|
||||
lockStatusEl.innerHTML = '<iconify-icon icon="mdi:lock" class="mr-1"></iconify-icon>锁定';
|
||||
lockStatusEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-red-50 text-red-600 inline-flex items-center';
|
||||
} else {
|
||||
lockStatusEl.innerText = '—';
|
||||
lockStatusEl.className = 'text-xs font-semibold text-gray-800';
|
||||
}
|
||||
|
||||
document.getElementById('det-packType').innerText = data.packType;
|
||||
document.getElementById('det-type').innerText = data.type;
|
||||
document.getElementById('det-outboundTime').innerText = data.outboundTime || '—';
|
||||
document.getElementById('det-itemNameCn').innerText = data.itemNameCn || '—';
|
||||
document.getElementById('det-itemNameEn').innerText = data.itemNameEn || '—';
|
||||
document.getElementById('det-remark').innerText = data.remark || '—';
|
||||
|
||||
// 件重信息
|
||||
document.getElementById('det-pieces').innerText = data.pieces;
|
||||
document.getElementById('det-weight').innerText = data.weight;
|
||||
document.getElementById('det-calcWeight').innerText = data.calcWeight;
|
||||
document.getElementById('det-inPieces').innerText = data.inPieces;
|
||||
document.getElementById('det-inWeight').innerText = data.inWeight;
|
||||
|
||||
// 海关放行标签
|
||||
const customsReleaseEl = document.getElementById('det-customsRelease');
|
||||
if (data.customsRelease === '11') {
|
||||
customsReleaseEl.innerText = '11';
|
||||
customsReleaseEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-green-50 text-green-600 inline-block';
|
||||
} else {
|
||||
customsReleaseEl.innerText = data.customsRelease;
|
||||
customsReleaseEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-gray-100 text-gray-600 inline-block';
|
||||
}
|
||||
document.getElementById('det-originalManifest').innerText = data.originalManifest;
|
||||
document.getElementById('det-arriReport').innerText = data.arriReport;
|
||||
document.getElementById('det-mftReport').innerText = data.mftReport;
|
||||
document.getElementById('det-tallyReport').innerText = data.tallyReport;
|
||||
// 渲染仓库批次
|
||||
const warehouseContainer = document.getElementById('tab-content-1');
|
||||
warehouseContainer.innerHTML = '';
|
||||
data.warehouse.forEach(item => {
|
||||
warehouseContainer.innerHTML += `
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="flex justify-between items-center pt-3 pb-1.5 px-3 mb-2 border-b border-gray-100">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block"></span>
|
||||
<span class="text-xs font-semibold text-gray-900">批次号: ${item.batch}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-1.5 px-3 pb-2">
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">件数</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.pieces}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">重量</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${parseFloat(item.weight).toFixed(1)} kg</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">航班日期</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.flightDate}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">航班号</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.flightNo}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">始发站</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.route}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">目的站</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.dest}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
// 渲染库位信息
|
||||
const locationContainer = document.getElementById('tab-content-2');
|
||||
locationContainer.innerHTML = '';
|
||||
data.locations.forEach(loc => {
|
||||
locationContainer.innerHTML += `
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="flex justify-between items-center pt-3 pb-1.5 px-3 mb-2 border-b border-gray-100">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block"></span>
|
||||
<span class="text-xs font-bold text-gray-900">${loc.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-1.5 px-3 pb-2">
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库人</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${loc.inPerson}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库时间</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${loc.inTime}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">出库人</div>
|
||||
<div class="text-xs ${loc.outPerson === '—' ? 'text-gray-500' : 'text-gray-800'} font-semibold">${loc.outPerson}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">出库时间</div>
|
||||
<div class="text-xs ${loc.outTime === '—' ? 'text-gray-500' : 'text-gray-800'} font-semibold">${loc.outTime}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
// 默认激活运单信息标签
|
||||
switchTab(0);
|
||||
// 页面切换动画:列表左滑隐藏,详情滑入
|
||||
listPage.classList.add('page-hidden-left');
|
||||
detailPage.classList.remove('page-hidden-right');
|
||||
}
|
||||
|
||||
// 返回列表页
|
||||
function goBack() {
|
||||
const listPage = document.getElementById('listPage');
|
||||
const detailPage = document.getElementById('detailPage');
|
||||
listPage.classList.remove('page-hidden-left');
|
||||
detailPage.classList.add('page-hidden-right');
|
||||
}
|
||||
|
||||
// 切换详情标签页
|
||||
function switchTab(index) {
|
||||
const tabs = document.querySelectorAll('.tab-item');
|
||||
const contents = document.querySelectorAll('.tab-pane');
|
||||
tabs.forEach((tab, i) => {
|
||||
if (i === index) {
|
||||
tab.classList.add('active');
|
||||
contents[i].classList.remove('hidden');
|
||||
} else {
|
||||
tab.classList.remove('active');
|
||||
contents[i].classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Toast提示
|
||||
function showToast(msg) {
|
||||
const toast = document.getElementById('toast');
|
||||
toast.innerText = msg;
|
||||
toast.classList.remove('hidden');
|
||||
setTimeout(() => toast.classList.add('hidden'), 2000);
|
||||
}
|
||||
|
||||
// 筛选弹窗方法
|
||||
function openFilter() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
filterOverlay.classList.add('show');
|
||||
setTimeout(() => filterSheet.classList.add('show'), 10);
|
||||
}
|
||||
function closeFilter() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
filterSheet.classList.remove('show');
|
||||
setTimeout(() => filterOverlay.classList.remove('show'), 250);
|
||||
}
|
||||
function resetFilter() {
|
||||
document.querySelectorAll('.filter-input').forEach(input => {
|
||||
input.value = '';
|
||||
});
|
||||
}
|
||||
function confirmFilter() {
|
||||
const params = {
|
||||
flightDate: document.getElementById('filterFlightDate')?.value || '',
|
||||
flightNo: document.getElementById('filterFlightNo')?.value || '',
|
||||
agent: '',
|
||||
code: '',
|
||||
origin: document.getElementById('filterOrigin')?.value || '',
|
||||
billType: '',
|
||||
bizType: '',
|
||||
cnName: document.getElementById('filterCnName')?.value || ''
|
||||
};
|
||||
console.log('筛选参数:', params);
|
||||
alert('筛选执行成功,控制台可查看筛选条件');
|
||||
closeFilter();
|
||||
}
|
||||
|
||||
// 扫码弹窗
|
||||
function toggleScanOverlay(show) {
|
||||
const overlay = document.getElementById('scanOverlay');
|
||||
if (show) overlay.classList.remove('hidden');
|
||||
else overlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
// 页面加载完成初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
renderList();
|
||||
// 筛选遮罩点击关闭
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
filterOverlay.addEventListener('click', function(e) {
|
||||
if (e.target === filterOverlay) closeFilter();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
139
documents/5.5寸手持端设计文件/07_出港仓库/运单追踪.html
Normal file
139
documents/5.5寸手持端设计文件/07_出港仓库/运单追踪.html
Normal file
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>国际进港查询 - 物流跟踪</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
src: local('sans-serif');
|
||||
}
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.no-scrollbar::-webkit-scrollbar { display: none; }
|
||||
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
|
||||
/* 模拟手机容器阴影 */
|
||||
.phone-shell {
|
||||
box-shadow: 0 50px 100px -20px rgba(0,0,0,0.25), 0 30px 60px -30px rgba(0,0,0,0.3);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-200 flex justify-center items-center min-h-screen p-4">
|
||||
<!-- 模拟手机屏幕容器 -->
|
||||
<div class="phone-shell w-[375px] h-[812px] bg-[#F5F5F7] overflow-hidden flex flex-col relative rounded-[48px] border-[10px] border-gray-900">
|
||||
<!-- 顶部蓝色区域 (包含状态栏和导航栏) -->
|
||||
<div class="bg-[#1A73E8] flex-none">
|
||||
<!-- 系统状态栏模拟 -->
|
||||
<div class="h-11 w-full flex justify-between items-center px-8 text-white">
|
||||
<span class="text-[15px] font-bold">09:41</span>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
<iconify-icon class="text-sm" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-base" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-xl" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<div class="h-[44px] px-4 flex items-center justify-between">
|
||||
<button class="flex items-center text-white space-x-0.5">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span class="text-base">返回</span>
|
||||
</button>
|
||||
<h1 class="text-lg font-bold text-white absolute left-1/2 -translate-x-1/2">运单追踪</h1>
|
||||
<div class="w-10"></div> <!-- 占位保持平衡 -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- 可滚动内容区 -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar px-5 pt-6 pb-12">
|
||||
<!-- 流转状态卡片 -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-5">
|
||||
<!-- 运单号 Header -->
|
||||
<div class="flex items-center justify-between pt-2 pb-5 mb-8 border-b border-gray-100">
|
||||
<div class="text-xl font-black text-gray-900 leading-none tracking-tight">
|
||||
78133358743
|
||||
</div>
|
||||
<span class="text-[11px] font-medium text-blue-600 bg-blue-50 px-2.5 py-1 rounded-full border border-blue-100">国际进港</span>
|
||||
</div>
|
||||
<!-- 流转状态标题 -->
|
||||
<div class="mb-6">
|
||||
<h2 class="text-base font-semibold text-gray-700 flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-gray-700 inline-block" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
流转状态
|
||||
</h2>
|
||||
</div>
|
||||
<div class="relative pl-7 pb-2">
|
||||
<div class="absolute left-[9px] top-2 bottom-4 w-[2px] bg-gray-100"></div>
|
||||
<!-- 状态 1: 当前 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[27px] top-1 w-[20px] h-[20px] rounded-full bg-green-600 border-4 border-green-100 flex items-center justify-center ring-1 ring-green-600/20">
|
||||
<div class="w-2 h-2 rounded-full bg-white"></div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-green-700">原始舱单</span>
|
||||
<span class="text-green-600 text-xs font-normal bg-green-50 px-2 py-0.5 rounded-full">放行状态:01</span>
|
||||
</div>
|
||||
<div class="text-gray-400 text-xs font-normal flex items-center gap-1.5">
|
||||
<span>2026-07-10 11:03:38</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 2 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">分拣理货</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 3 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">理货申报</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 4 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">海关放行</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 5 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">柜台办结</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 6 -->
|
||||
<div class="relative mb-2">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">提取出库</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 模块 4: 操作详情卡片 -> 已删除 -->
|
||||
<!-- 底部占位 -->
|
||||
<div class="text-center py-6">
|
||||
<p class="text-[10px] text-gray-300 uppercase tracking-[0.2em] font-bold">查询信息已触底</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部 Home Indicator 模拟 -->
|
||||
<div class="h-8 w-full flex justify-center items-center flex-none bg-white/60 backdrop-blur-md">
|
||||
<div class="w-32 h-1.5 bg-black rounded-full opacity-20"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>ml>
|
||||
139
documents/5.5寸手持端设计文件/08_进港仓库/运单追踪.html
Normal file
139
documents/5.5寸手持端设计文件/08_进港仓库/运单追踪.html
Normal file
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>国际进港查询 - 物流跟踪</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
src: local('sans-serif');
|
||||
}
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.no-scrollbar::-webkit-scrollbar { display: none; }
|
||||
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
|
||||
/* 模拟手机容器阴影 */
|
||||
.phone-shell {
|
||||
box-shadow: 0 50px 100px -20px rgba(0,0,0,0.25), 0 30px 60px -30px rgba(0,0,0,0.3);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-200 flex justify-center items-center min-h-screen p-4">
|
||||
<!-- 模拟手机屏幕容器 -->
|
||||
<div class="phone-shell w-[375px] h-[812px] bg-[#F5F5F7] overflow-hidden flex flex-col relative rounded-[48px] border-[10px] border-gray-900">
|
||||
<!-- 顶部蓝色区域 (包含状态栏和导航栏) -->
|
||||
<div class="bg-[#1A73E8] flex-none">
|
||||
<!-- 系统状态栏模拟 -->
|
||||
<div class="h-11 w-full flex justify-between items-center px-8 text-white">
|
||||
<span class="text-[15px] font-bold">09:41</span>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
<iconify-icon class="text-sm" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-base" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-xl" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<div class="h-[44px] px-4 flex items-center justify-between">
|
||||
<button class="flex items-center text-white space-x-0.5">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span class="text-base">返回</span>
|
||||
</button>
|
||||
<h1 class="text-lg font-bold text-white absolute left-1/2 -translate-x-1/2">运单追踪</h1>
|
||||
<div class="w-10"></div> <!-- 占位保持平衡 -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- 可滚动内容区 -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar px-5 pt-6 pb-12">
|
||||
<!-- 流转状态卡片 -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-5">
|
||||
<!-- 运单号 Header -->
|
||||
<div class="flex items-center justify-between pt-2 pb-5 mb-8 border-b border-gray-100">
|
||||
<div class="text-xl font-black text-gray-900 leading-none tracking-tight">
|
||||
78133358743
|
||||
</div>
|
||||
<span class="text-[11px] font-medium text-blue-600 bg-blue-50 px-2.5 py-1 rounded-full border border-blue-100">国际进港</span>
|
||||
</div>
|
||||
<!-- 流转状态标题 -->
|
||||
<div class="mb-6">
|
||||
<h2 class="text-base font-semibold text-gray-700 flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-gray-700 inline-block" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
流转状态
|
||||
</h2>
|
||||
</div>
|
||||
<div class="relative pl-7 pb-2">
|
||||
<div class="absolute left-[9px] top-2 bottom-4 w-[2px] bg-gray-100"></div>
|
||||
<!-- 状态 1: 当前 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[27px] top-1 w-[20px] h-[20px] rounded-full bg-green-600 border-4 border-green-100 flex items-center justify-center ring-1 ring-green-600/20">
|
||||
<div class="w-2 h-2 rounded-full bg-white"></div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-green-700">原始舱单</span>
|
||||
<span class="text-green-600 text-xs font-normal bg-green-50 px-2 py-0.5 rounded-full">放行状态:01</span>
|
||||
</div>
|
||||
<div class="text-gray-400 text-xs font-normal flex items-center gap-1.5">
|
||||
<span>2026-07-10 11:03:38</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 2 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">分拣理货</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 3 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">理货申报</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 4 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">海关放行</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 5 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">柜台办结</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 6 -->
|
||||
<div class="relative mb-2">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">提取出库</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 模块 4: 操作详情卡片 -> 已删除 -->
|
||||
<!-- 底部占位 -->
|
||||
<div class="text-center py-6">
|
||||
<p class="text-[10px] text-gray-300 uppercase tracking-[0.2em] font-bold">查询信息已触底</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部 Home Indicator 模拟 -->
|
||||
<div class="h-8 w-full flex justify-center items-center flex-none bg-white/60 backdrop-blur-md">
|
||||
<div class="w-32 h-1.5 bg-black rounded-full opacity-20"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>ml>
|
||||
924
documents/5.5寸手持端设计文件/08_进港仓库/进港仓库.html
Normal file
924
documents/5.5寸手持端设计文件/08_进港仓库/进港仓库.html
Normal file
@@ -0,0 +1,924 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>进港仓库</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'PingFang SC';
|
||||
src: local('PingFang SC');
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #e5e7eb;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 隐藏滚动条但保留功能 */
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.hide-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.tag-occupied { background-color: #dcfce7; color: #22c55e; } /* 已离港 - 绿色 */
|
||||
.tag-available { background-color: #dcfce7; color: #22c55e; } /* 已离港 - 绿色 */
|
||||
.tag-repair { background-color: #fce7e7; color: #ef4444; } /* 离港 - 红色 */
|
||||
|
||||
/* 页面切换动画 */
|
||||
.page {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
background-color: #f3f4f6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.page::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.page-hidden-right { transform: translateX(100%); }
|
||||
.page-hidden-left { transform: translateX(-100%); }
|
||||
|
||||
|
||||
/* 统一的深蓝色调 */
|
||||
.bg-header-blue { background-color: #2563EB; }
|
||||
|
||||
|
||||
/* 标签页样式 */
|
||||
.tab-item {
|
||||
position: relative;
|
||||
padding: 12px 0;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
.tab-item.active {
|
||||
color: #2563eb;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tab-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background-color: #2563eb;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.tab-item.active::after {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
/* ========== 筛选弹窗样式 提升层级+动画稳定 ========== */
|
||||
.filter-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.25s ease, visibility 0.25s ease;
|
||||
}
|
||||
.filter-overlay.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.filter-sheet {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #fff;
|
||||
border-top-left-radius: 20px;
|
||||
border-top-right-radius: 20px;
|
||||
padding: 20px;
|
||||
z-index: 10000;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.25s ease-out;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.filter-sheet.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
/* 筛选输入框统一样式 */
|
||||
.filter-input {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.filter-input:focus {
|
||||
border-color: #2563EB;
|
||||
}
|
||||
|
||||
/* 下拉框箭头浅灰色 + 全浏览器兼容修复 */
|
||||
select.filter-input {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%239ca3af' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10l-5 5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 12px center;
|
||||
background-size: 16px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
/* 清除IE默认下拉箭头 */
|
||||
select.filter-input::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes scan {
|
||||
0% { top: 0; }
|
||||
100% { top: 100%; }
|
||||
}
|
||||
.scan-line {
|
||||
animation: scan 2s linear infinite;
|
||||
}
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.bottom-drawer {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="w-[375px] h-[812px] bg-[#F3F4F6] overflow-hidden flex flex-col relative shadow-2xl rounded-[40px] border-[8px] border-gray-800">
|
||||
<!-- 状态栏 -->
|
||||
<div class="h-6 w-full bg-[#2563EB] flex justify-between items-center px-6 text-white text-xs pt-1">
|
||||
<span>09:41</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<iconify-icon icon="material-symbols:signal-cellular-4-bar-rounded"></iconify-icon>
|
||||
<iconify-icon icon="material-symbols:wifi"></iconify-icon>
|
||||
<iconify-icon icon="material-symbols:battery-full-rounded"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 筛选弹窗容器 移到最外层,层级提升 -->
|
||||
<div class="filter-overlay" id="filterOverlay"></div>
|
||||
<div class="filter-sheet" id="filterSheet">
|
||||
<!-- 顶部标题栏 -->
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="closeFilter()">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
<h2 class="text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
</div>
|
||||
<!-- 筛选表单区域 -->
|
||||
<div class="space-y-4 mb-7">
|
||||
<!-- 1.航班日期 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班日期</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择航班日期" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 2.航班号 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班号</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入航班号" type="text" id="filterFlightNo">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 3.代理 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">代理</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择代理" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 4.特码 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">特码</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择特码" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 5.始发站 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">始发站</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入始发站" type="text" id="filterOrigin">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 6.运单类型 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">运单类型</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择运单类型" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 7.业务类型 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">业务类型</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择业务类型" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 8.品名(中) -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">品名(中)</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入中文品名" type="text" id="filterCnName">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部操作按钮 -->
|
||||
<div class="flex gap-3">
|
||||
<button class="flex-1 h-11 bg-gray-100 text-gray-600 rounded-lg text-sm font-medium" onclick="resetFilter()">重置</button>
|
||||
<button class="flex-1 h-11 bg-[#2563EB] text-white rounded-lg text-sm font-medium" onclick="confirmFilter()">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面主容器 -->
|
||||
<div class="flex-1 relative overflow-hidden">
|
||||
<!-- 列表首页:进港仓库卡片页面 -->
|
||||
<div class="page z-10" id="listPage">
|
||||
<!-- 顶部导航栏 -->
|
||||
<header class="bg-[#2563EB] h-14 flex items-center justify-between px-4 flex-none">
|
||||
<button class="flex items-center text-white text-sm" onclick="showToast('返回上一级')">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="text-white font-bold text-lg">进港仓库</h1>
|
||||
<div class="w-12"></div>
|
||||
</header>
|
||||
<!-- 搜索区 -->
|
||||
<section class="bg-[#F3F4F6] p-3 flex-none">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex-1 relative">
|
||||
<iconify-icon class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-lg" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="w-full bg-white rounded-full pl-10 pr-10 py-2.5 text-sm shadow-sm border border-gray-200 outline-none" onkeypress="if(event.keyCode==13) showToast('执行搜索')" placeholder="搜索运单号/航班号..." type="text"/>
|
||||
<button class="absolute right-2 top-1/2 -translate-y-1/2 p-1 text-[#2563EB]" onclick="toggleScanOverlay(true)">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button id="filterBtn" class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="openFilter()">
|
||||
<iconify-icon class="text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<!-- 统计栏 -->
|
||||
<section class="px-3 pb-3 flex justify-between gap-2 flex-none">
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#2563EB]">
|
||||
<div class="text-[11px] text-gray-500">总票数</div>
|
||||
<div class="text-lg font-bold text-[#2563EB]">8</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-orange-500">
|
||||
<div class="text-[11px] text-gray-500">未清仓</div>
|
||||
<div class="text-lg font-bold text-orange-500">4</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#10B981]">
|
||||
<div class="text-[11px] text-gray-500">已清仓</div>
|
||||
<div class="text-lg font-bold text-[#10B981]">4</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- 卡片列表容器 -->
|
||||
<main class="flex-1 overflow-y-auto px-3 pb-4 custom-scrollbar" id="cardListContainer">
|
||||
</main>
|
||||
<!-- 扫码层 -->
|
||||
<div class="absolute inset-0 z-50 bg-black hidden flex flex-col" id="scanOverlay">
|
||||
<div class="p-6 flex justify-between items-center text-white">
|
||||
<button onclick="toggleScanOverlay(false)">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
<span class="text-sm font-medium">扫描运单条码</span>
|
||||
<button><iconify-icon class="text-2xl" icon="mdi:flashlight"></iconify-icon></button>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col items-center justify-center p-12">
|
||||
<div class="w-64 h-64 border-2 border-[#2563EB] relative overflow-hidden rounded-lg">
|
||||
<div class="absolute top-0 left-0 w-full h-0.5 bg-[#2563EB] shadow-[0_0_10px_#2563EB] scan-line"></div>
|
||||
<div class="absolute top-0 left-0 w-6 h-6 border-t-4 border-l-4 border-[#2563EB]"></div>
|
||||
<div class="absolute top-0 right-0 w-6 h-6 border-t-4 border-r-4 border-[#2563EB]"></div>
|
||||
<div class="absolute bottom-0 left-0 w-6 h-6 border-b-4 border-l-4 border-[#2563EB]"></div>
|
||||
<div class="absolute bottom-0 right-0 w-6 h-6 border-b-4 border-r-4 border-[#2563EB]"></div>
|
||||
</div>
|
||||
<p class="text-white text-xs mt-6 opacity-60 text-center px-4">将条码/二维码放入框内,即可自动扫描</p>
|
||||
</div>
|
||||
<div class="h-32 bg-black flex justify-center items-center gap-12 text-white">
|
||||
<div class="flex flex-col items-center opacity-60">
|
||||
<iconify-icon class="text-3xl" icon="mdi:image-outline"></iconify-icon>
|
||||
<span class="text-[10px] mt-1">相册</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<iconify-icon class="text-3xl" icon="mdi:keyboard-outline"></iconify-icon>
|
||||
<span class="text-[10px] mt-1">手动输入</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toast -->
|
||||
<div class="absolute bottom-20 left-1/2 -translate-x-1/2 bg-black bg-opacity-80 text-white text-xs px-4 py-2 rounded-full hidden z-[60] whitespace-nowrap" id="toast">提示内容</div>
|
||||
</div>
|
||||
|
||||
<!-- 进港货物页面(右滑弹出) -->
|
||||
<div class="page z-20 page-hidden-right !bg-[#f0f2f5]" id="detailPage">
|
||||
<!-- 头部 -->
|
||||
<header class="bg-header-blue pt-2 pb-4 px-4 flex items-center relative flex-none">
|
||||
<button class="flex items-center text-white text-sm absolute left-4 active:opacity-70" onclick="goBack()">
|
||||
<iconify-icon class="text-xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="mx-auto text-white font-bold text-lg">进港货物</h1>
|
||||
<div class="absolute right-4 flex items-center">
|
||||
<iconify-icon class="text-red-500 text-2xl" icon="mdi:file-document-edit-outline"></iconify-icon>
|
||||
</div>
|
||||
</header>
|
||||
<!-- 标签页 -->
|
||||
<div class="bg-white px-4 border-b border-gray-200 flex-none">
|
||||
<div class="flex justify-around">
|
||||
<div class="tab-item active cursor-pointer" onclick="switchTab(0)">运单信息</div>
|
||||
<div class="tab-item cursor-pointer" onclick="switchTab(1)">仓库信息</div>
|
||||
<div class="tab-item cursor-pointer" onclick="switchTab(2)">库位信息</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 详情内容区 -->
|
||||
<div class="flex-1 overflow-y-auto hide-scrollbar">
|
||||
<!-- 运单信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3" id="tab-content-0">
|
||||
<!-- 卡片1:基本信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800" id="det-uldTitle">基本信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-2 gap-2">
|
||||
<!-- Row 1 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-billNo"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">代理人</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-agent"></span>
|
||||
</div>
|
||||
<!-- Row 2 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">特码</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-code"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">承运人</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-carrier"></span>
|
||||
</div>
|
||||
<!-- Row 3 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">始发站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-route"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">中转站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-outboundTime"></span>
|
||||
</div>
|
||||
<!-- Row 4 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">目的站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-dest"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">UN编号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-unNo"></span>
|
||||
</div>
|
||||
<!-- Row 5 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center" id="det-lockStatus-container">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5" id="det-lockStatus-label">锁定状态</span>
|
||||
<div id="det-lockStatus-wrapper">
|
||||
<span class="text-xs font-semibold" id="det-lockStatus"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">包装类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-packType"></span>
|
||||
</div>
|
||||
<!-- Row 6 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">业务类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-type"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库时间</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-ciTime"></span>
|
||||
</div>
|
||||
<!-- Row 7 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-billType"></span>
|
||||
</div>
|
||||
<!-- Row 8 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">品名(中)</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-itemNameCn"></span>
|
||||
</div>
|
||||
<!-- Row 9 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">品名(英)</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-itemNameEn"></span>
|
||||
</div>
|
||||
<!-- Row 10 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">备注</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-remark"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片2:件重信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800">件重信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-3 gap-2">
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单件数</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-pieces"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-weight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">计费重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-calcWeight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库件数</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-inPieces"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-inWeight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center opacity-0">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">—</div>
|
||||
<div class="text-xs text-gray-800 font-semibold">—</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片3:回执信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800">回执信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-3 gap-2">
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">原始舱单</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-originalManifest"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">理货报告</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-tallyReport"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center" id="det-customsRelease-container">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">海关放行</div>
|
||||
<div id="det-customsRelease-wrapper">
|
||||
<div class="text-xs font-semibold" id="det-customsRelease"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 仓库信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3 hidden" id="tab-content-1"></div>
|
||||
<!-- 库位信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3 hidden" id="tab-content-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 运单详情数据源(用于进港货物渲染)
|
||||
const uldData = {
|
||||
'78133363396': {
|
||||
billNo: '78133363396', agent: '中外运', code: 'DGR',
|
||||
pieces: '444', weight: '555.0', type: '普通货物运输',
|
||||
inPieces: '444', inWeight: '555.0', calcWeight: '555.0',
|
||||
carrier: 'CA', route: 'LAX', packType: '纸箱',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'HFE',
|
||||
itemNameCn: '电子元器件', itemNameEn: 'Electronic Components',
|
||||
lockStatus: '未锁定', outboundTime: '—', remark: '需冷藏存储',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',
|
||||
warehouse: [
|
||||
{ batch: '1', pieces: 200, weight: 300.0, flightDate: '2026-07-08', flightNo: 'CA1234',route: 'LAX',dest: 'HFE', time: '2026-07-08 14:30:00' },
|
||||
{ batch: '2', pieces: 244, weight: 255.0, flightDate: '2026-07-09', flightNo: 'CA5678',route: 'LAX',dest: 'HFE', time: '2026-07-09 09:10:46' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'A-01-001', inPerson: '张三', inTime: '2026-07-08 14:30', outPerson: '李四', outTime: '2026-07-09 10:00' },
|
||||
{ id: 'B-02-005', inPerson: '王五', inTime: '2026-07-09 09:10', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363397': {
|
||||
billNo: '78133363397', agent: '徽远', code: 'PER',
|
||||
pieces: '120', weight: '320.0', type: '普通货物运输',
|
||||
inPieces: '120', inWeight: '320.0', calcWeight: '320.0',
|
||||
carrier: 'MU', route: 'PVG-NRT', packType: '托盘',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'PVG',
|
||||
itemNameCn: '服装', itemNameEn: 'Garments',
|
||||
lockStatus: '未锁定', outboundTime: '—', remark: '易碎品,轻拿轻放',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',
|
||||
warehouse: [
|
||||
{ batch: '3', pieces: 60, weight: 150.0, flightDate: '2026-07-07', flightNo: 'MU123', time: '2026-07-07 16:20:00' },
|
||||
{ batch: '4', pieces: 60, weight: 170.0, flightDate: '2026-07-08', flightNo: 'MU456', time: '2026-07-08 11:00:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'A-03-012', inPerson: '赵六', inTime: '2026-07-07 16:20', outPerson: '钱七', outTime: '2026-07-08 14:00' },
|
||||
{ id: 'C-01-008', inPerson: '孙八', inTime: '2026-07-08 11:00', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363398': {
|
||||
billNo: '78133363398', agent: '中外运', code: 'NOR',
|
||||
pieces: '268', weight: '780.0', type: '普通货物运输',
|
||||
inPieces: '268', inWeight: '780.0', calcWeight: '780.0',
|
||||
carrier: 'CZ', route: 'CAN-AMS', packType: '托盘',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'CAN',
|
||||
itemNameCn: '机械设备', itemNameEn: 'Machinery Equipment',
|
||||
lockStatus: '锁定', outboundTime: '2026-07-09 08:00', remark: '需叉车装卸',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',
|
||||
warehouse: [
|
||||
{ batch: '5', pieces: 150, weight: 420.0, flightDate: '2026-07-08', flightNo: 'CZ789', time: '2026-07-08 22:15:00' },
|
||||
{ batch: '6', pieces: 118, weight: 360.0, flightDate: '2026-07-09', flightNo: 'CZ012', time: '2026-07-09 06:30:00' },
|
||||
{ batch: '7', pieces: 100, weight: 200.0, flightDate: '2026-07-09', flightNo: 'CZ345', time: '2026-07-09 13:00:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'B-01-003', inPerson: '张三', inTime: '2026-07-08 22:15', outPerson: '李四', outTime: '2026-07-09 06:00' },
|
||||
{ id: 'A-02-010', inPerson: '王五', inTime: '2026-07-09 06:30', outPerson: '—', outTime: '—' },
|
||||
{ id: 'C-03-007', inPerson: '赵六', inTime: '2026-07-09 13:00', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363399': {
|
||||
billNo: '78133363399', agent: '徽远', code: 'DGR',
|
||||
pieces: '85', weight: '210.0', type: '危险品运输',
|
||||
inPieces: '85', inWeight: '210.0', calcWeight: '210.0',
|
||||
carrier: '3U', route: 'CTU-SIN', packType: '铁箱',
|
||||
billType: '国际进港(国际直航)', unNo: 'UN3480', dest: 'CTU',
|
||||
itemNameCn: '锂电池', itemNameEn: 'Lithium Battery',
|
||||
lockStatus: '锁定', outboundTime: '—', remark: '危险品,单独存放',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',
|
||||
warehouse: [
|
||||
{ batch: '8', pieces: 45, weight: 110.0, flightDate: '2026-07-08', flightNo: '3U678', time: '2026-07-08 18:00:00' },
|
||||
{ batch: '9', pieces: 40, weight: 100.0, flightDate: '2026-07-09', flightNo: '3U901', time: '2026-07-09 07:45:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'D-01-002', inPerson: '钱七', inTime: '2026-07-08 18:00', outPerson: '孙八', outTime: '2026-07-09 08:00' },
|
||||
{ id: 'A-04-015', inPerson: '张三', inTime: '2026-07-09 07:45', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363400': {
|
||||
billNo: '78133363400', agent: '中外运', code: 'PER',
|
||||
pieces: '350', weight: '430.0', type: '普通货物运输',
|
||||
inPieces: '350', inWeight: '430.0', calcWeight: '430.0',
|
||||
carrier: 'HU', route: 'HKG-HFE', packType: '纸箱',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'HFE',
|
||||
itemNameCn: '食品', itemNameEn: 'Food Products',
|
||||
lockStatus: '未锁定', outboundTime: '2026-07-09 11:30', remark: '保质期至2026-12',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',ciTime: '2026-07-08 14:30:00',
|
||||
warehouse: [
|
||||
{ batch: '10', pieces: 180, weight: 220.0, flightDate: '2026-07-07', flightNo: 'HU234', time: '2026-07-07 20:00:00' },
|
||||
{ batch: '11', pieces: 170, weight: 210.0, flightDate: '2026-07-09', flightNo: 'HU567', time: '2026-07-09 10:30:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'B-02-011', inPerson: '李四', inTime: '2026-07-07 20:00', outPerson: '王五', outTime: '2026-07-08 16:00' },
|
||||
{ id: 'C-01-009', inPerson: '赵六', inTime: '2026-07-09 10:30', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// 进港仓库卡片列表数据源
|
||||
const data = [
|
||||
{ id: "78133363396", status: "已清仓", flight: "20260702/MU1111", route: "LAX-HFE", pcs: 111, weight: 2300 },
|
||||
{ id: "78133363397", status: "未清仓", flight: "20260702/MU2222", route: "NRT-HFE", pcs: 85, weight: 1560 },
|
||||
{ id: "78133363398", status: "已清仓", flight: "20260702/MU3333", route: "FRA-HFE", pcs: 200, weight: 4200 },
|
||||
{ id: "78133363399", status: "未清仓", flight: "20260702/MU4444", route: "LAX-PVG", pcs: 50, weight: 980 },
|
||||
{ id: "78133363400", status: "未清仓", flight: "20260702/MU5555", route: "CDG-HFE", pcs: 168, weight: 3150 },
|
||||
{ id: "78133363401", status: "已清仓", flight: "20260702/MU6666", route: "SIN-HFE", pcs: 92, weight: 1800 },
|
||||
{ id: "78133363402", status: "未清仓", flight: "20260702/MU7777", route: "LAX-NKG", pcs: 145, weight: 2800 },
|
||||
{ id: "78133363403", status: "已清仓", flight: "20260702/MU8888", route: "HND-HFE", pcs: 73, weight: 1420 }
|
||||
];
|
||||
|
||||
// 渲染仓库卡片列表
|
||||
function renderList() {
|
||||
const container = document.getElementById('cardListContainer');
|
||||
container.innerHTML = data.map((item) => {
|
||||
const isCleared = item.status === '已清仓';
|
||||
const departure = item.route.split('-')[0];
|
||||
const flightNo = item.flight.split('/')[1];
|
||||
return `
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer mb-3" onclick="showDetail('${item.id}')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">${item.id}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
${getStatusTag(item.status)}
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:${item.pcs}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:${item.weight}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span>航班号:${flightNo}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span>始发站:${departure}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 gap-2 mt-3">
|
||||
<button onclick="event.stopPropagation(); showToast('执行清仓操作:运单号 ${item.id}')"
|
||||
class="text-xs py-1.5 px-2 rounded-[6px] font-medium h-8 ${isCleared ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-[#EFF6FF] text-[#2563EB]'}" ${isCleared ? 'disabled' : ''}>
|
||||
清仓
|
||||
</button>
|
||||
<button onclick="event.stopPropagation(); showToast('修改库位:运单号 ${item.id}')"
|
||||
class="bg-[#EFF6FF] text-[#2563EB] text-xs py-1.5 px-2 rounded-[6px] font-medium h-8">
|
||||
修改库位
|
||||
</button>
|
||||
<button onclick="event.stopPropagation(); showToast('执行出库:运单号 ${item.id}')"
|
||||
class="bg-[#EFF6FF] text-[#2563EB] text-xs py-1.5 px-2 rounded-[6px] font-medium h-8">
|
||||
出库
|
||||
</button>
|
||||
<button onclick="event.stopPropagation(); showToast('执行入库:运单号 ${item.id}')"
|
||||
class="bg-[#EFF6FF] text-[#2563EB] text-xs py-1.5 px-2 rounded-[6px] font-medium h-8">
|
||||
入库
|
||||
</button>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// 状态标签渲染
|
||||
function getStatusTag(status) {
|
||||
let color = '';
|
||||
switch(status) {
|
||||
case '已清仓': color = 'bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium'; break;
|
||||
case '未清仓': color = 'bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium'; break;
|
||||
}
|
||||
return `<span class="${color}">${status}</span>`;
|
||||
}
|
||||
|
||||
// 跳转至进港货物页面,填充数据
|
||||
function showDetail(id) {
|
||||
const listPage = document.getElementById('listPage');
|
||||
const detailPage = document.getElementById('detailPage');
|
||||
|
||||
const data = uldData[id];
|
||||
if (!data) {
|
||||
showToast("暂无该运单详情数据");
|
||||
return;
|
||||
}
|
||||
// 填充基础信息
|
||||
document.getElementById('det-billNo').innerText = data.billNo;
|
||||
document.getElementById('det-agent').innerText = data.agent;
|
||||
document.getElementById('det-code').innerText = data.code;
|
||||
document.getElementById('det-carrier').innerText = data.carrier;
|
||||
document.getElementById('det-route').innerText = data.route;
|
||||
document.getElementById('det-dest').innerText = data.dest;
|
||||
document.getElementById('det-billType').innerText = data.billType;
|
||||
document.getElementById('det-unNo').innerText = data.unNo;
|
||||
document.getElementById('det-ciTime').innerText = data.ciTime;
|
||||
// 锁定状态样式
|
||||
const lockStatusEl = document.getElementById('det-lockStatus');
|
||||
if (data.lockStatus === '锁定') {
|
||||
lockStatusEl.innerHTML = '<iconify-icon icon="mdi:lock" class="mr-1"></iconify-icon>锁定';
|
||||
lockStatusEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-red-50 text-red-600 inline-flex items-center';
|
||||
} else {
|
||||
lockStatusEl.innerText = '—';
|
||||
lockStatusEl.className = 'text-xs font-semibold text-gray-800';
|
||||
}
|
||||
|
||||
document.getElementById('det-packType').innerText = data.packType;
|
||||
document.getElementById('det-type').innerText = data.type;
|
||||
document.getElementById('det-outboundTime').innerText = data.outboundTime || '—';
|
||||
document.getElementById('det-itemNameCn').innerText = data.itemNameCn || '—';
|
||||
document.getElementById('det-itemNameEn').innerText = data.itemNameEn || '—';
|
||||
document.getElementById('det-remark').innerText = data.remark || '—';
|
||||
|
||||
// 件重信息
|
||||
document.getElementById('det-pieces').innerText = data.pieces;
|
||||
document.getElementById('det-weight').innerText = data.weight;
|
||||
document.getElementById('det-calcWeight').innerText = data.calcWeight;
|
||||
document.getElementById('det-inPieces').innerText = data.inPieces;
|
||||
document.getElementById('det-inWeight').innerText = data.inWeight;
|
||||
|
||||
// 海关放行标签
|
||||
const customsReleaseEl = document.getElementById('det-customsRelease');
|
||||
if (data.customsRelease === '11') {
|
||||
customsReleaseEl.innerText = '11';
|
||||
customsReleaseEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-green-50 text-green-600 inline-block';
|
||||
} else {
|
||||
customsReleaseEl.innerText = data.customsRelease;
|
||||
customsReleaseEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-gray-100 text-gray-600 inline-block';
|
||||
}
|
||||
document.getElementById('det-originalManifest').innerText = data.originalManifest;
|
||||
document.getElementById('det-tallyReport').innerText = data.tallyReport;
|
||||
|
||||
// 渲染仓库批次
|
||||
const warehouseContainer = document.getElementById('tab-content-1');
|
||||
warehouseContainer.innerHTML = '';
|
||||
data.warehouse.forEach(item => {
|
||||
warehouseContainer.innerHTML += `
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="flex justify-between items-center pt-3 pb-1.5 px-3 mb-2 border-b border-gray-100">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block"></span>
|
||||
<span class="text-xs font-semibold text-gray-900">批次号: ${item.batch}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-1.5 px-3 pb-2">
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">件数</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.pieces}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">重量</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${parseFloat(item.weight).toFixed(1)} kg</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">航班日期</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.flightDate}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">航班号</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.flightNo}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">始发站</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.route}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">目的站</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.dest}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
// 渲染库位信息
|
||||
const locationContainer = document.getElementById('tab-content-2');
|
||||
locationContainer.innerHTML = '';
|
||||
data.locations.forEach(loc => {
|
||||
locationContainer.innerHTML += `
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="flex justify-between items-center pt-3 pb-1.5 px-3 mb-2 border-b border-gray-100">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block"></span>
|
||||
<span class="text-xs font-bold text-gray-900">${loc.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-1.5 px-3 pb-2">
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库人</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${loc.inPerson}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库时间</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${loc.inTime}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">出库人</div>
|
||||
<div class="text-xs ${loc.outPerson === '—' ? 'text-gray-500' : 'text-gray-800'} font-semibold">${loc.outPerson}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">出库时间</div>
|
||||
<div class="text-xs ${loc.outTime === '—' ? 'text-gray-500' : 'text-gray-800'} font-semibold">${loc.outTime}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
// 默认激活运单信息标签
|
||||
switchTab(0);
|
||||
// 页面切换动画:列表左滑隐藏,详情滑入
|
||||
listPage.classList.add('page-hidden-left');
|
||||
detailPage.classList.remove('page-hidden-right');
|
||||
}
|
||||
|
||||
// 返回列表页
|
||||
function goBack() {
|
||||
const listPage = document.getElementById('listPage');
|
||||
const detailPage = document.getElementById('detailPage');
|
||||
listPage.classList.remove('page-hidden-left');
|
||||
detailPage.classList.add('page-hidden-right');
|
||||
}
|
||||
|
||||
// 切换详情标签页
|
||||
function switchTab(index) {
|
||||
const tabs = document.querySelectorAll('.tab-item');
|
||||
const contents = document.querySelectorAll('.tab-pane');
|
||||
tabs.forEach((tab, i) => {
|
||||
if (i === index) {
|
||||
tab.classList.add('active');
|
||||
contents[i].classList.remove('hidden');
|
||||
} else {
|
||||
tab.classList.remove('active');
|
||||
contents[i].classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Toast提示
|
||||
function showToast(msg) {
|
||||
const toast = document.getElementById('toast');
|
||||
toast.innerText = msg;
|
||||
toast.classList.remove('hidden');
|
||||
setTimeout(() => toast.classList.add('hidden'), 2000);
|
||||
}
|
||||
|
||||
// 筛选弹窗方法
|
||||
function openFilter() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
filterOverlay.classList.add('show');
|
||||
setTimeout(() => filterSheet.classList.add('show'), 10);
|
||||
}
|
||||
function closeFilter() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
filterSheet.classList.remove('show');
|
||||
setTimeout(() => filterOverlay.classList.remove('show'), 250);
|
||||
}
|
||||
function resetFilter() {
|
||||
document.querySelectorAll('.filter-input').forEach(input => {
|
||||
input.value = '';
|
||||
});
|
||||
}
|
||||
function confirmFilter() {
|
||||
const params = {
|
||||
flightDate: document.getElementById('filterFlightDate')?.value || '',
|
||||
flightNo: document.getElementById('filterFlightNo')?.value || '',
|
||||
agent: '',
|
||||
code: '',
|
||||
origin: document.getElementById('filterOrigin')?.value || '',
|
||||
billType: '',
|
||||
bizType: '',
|
||||
cnName: document.getElementById('filterCnName')?.value || ''
|
||||
};
|
||||
console.log('筛选参数:', params);
|
||||
alert('筛选执行成功,控制台可查看筛选条件');
|
||||
closeFilter();
|
||||
}
|
||||
|
||||
// 扫码弹窗
|
||||
function toggleScanOverlay(show) {
|
||||
const overlay = document.getElementById('scanOverlay');
|
||||
if (show) overlay.classList.remove('hidden');
|
||||
else overlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
// 页面加载完成初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
renderList();
|
||||
// 筛选遮罩点击关闭
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
filterOverlay.addEventListener('click', function(e) {
|
||||
if (e.target === filterOverlay) closeFilter();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
773
documents/5.5寸手持端设计文件/09_提取出库/提取出库.html
Normal file
773
documents/5.5寸手持端设计文件/09_提取出库/提取出库.html
Normal file
@@ -0,0 +1,773 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>提取出库 - 移动端原型</title>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<!-- Iconify -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'PingFang SC';
|
||||
src: local('PingFang SC');
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #e2e8f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
/* 模拟手机容器 */
|
||||
.phone-container {
|
||||
width: 375px;
|
||||
height: 812px;
|
||||
background-color: #f5f6fa;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
border: 8px solid #1a1a1a;
|
||||
border-radius: 40px;
|
||||
}
|
||||
/* 隐藏滚动条但保留功能 */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
/* 页面切换动画 */
|
||||
.page {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
.page.active {
|
||||
display: flex;
|
||||
}
|
||||
/* 筛选面板动画 */
|
||||
#filter-panel {
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
#filter-panel.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.overlay {
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
.overlay.show {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
/* 按钮点击态 */
|
||||
.btn-active:active {
|
||||
opacity: 0.7;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
/* 复选框样式 */
|
||||
.custom-checkbox {
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #cbd5e1;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.custom-checkbox:checked {
|
||||
background-color: #2563EB;
|
||||
border-color: #2563EB;
|
||||
}
|
||||
.custom-checkbox:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.custom-checkbox-round {
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #cbd5e1;
|
||||
border-radius: 50%;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.custom-checkbox-round:checked {
|
||||
background-color: #2563EB;
|
||||
border-color: #2563EB;
|
||||
}
|
||||
.custom-checkbox-round:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
/* 卡片选中样式已移除 */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="phone-container">
|
||||
<!-- 状态栏模拟 -->
|
||||
<div class="h-10 w-full flex justify-between items-center px-8 flex-none bg-[#2563EB] text-white">
|
||||
<span class="text-xs font-semibold">09:41</span>
|
||||
<div class="flex space-x-1 items-center">
|
||||
<iconify-icon icon="mdi:signal" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 页面1: 货运列表首页 -->
|
||||
<div class="page active" id="page-home">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="bg-[#2563EB] text-white h-12 flex items-center justify-between px-4 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="alert('返回首页')">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-bold -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium">提取出库</h1>
|
||||
<div class="w-16"></div>
|
||||
</div>
|
||||
<!-- 内容滚动区 -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar pb-24">
|
||||
<!-- 搜索栏 -->
|
||||
<section class="px-4 pt-4 pb-3 bg-white">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center bg-white border border-gray-200 shadow-sm rounded-[12px] h-10 px-3 gap-2 flex-1">
|
||||
<iconify-icon class="text-gray-400 text-lg flex-none" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none rounded-lg px-2" placeholder="搜索运单号" type="text"/>
|
||||
<button class="flex-none text-blue-500">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="toggleFilterDrawer(true)">
|
||||
<iconify-icon class="text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<!-- 子Tab切换栏 -->
|
||||
<div class="flex bg-white border-b sticky top-0 z-20">
|
||||
<button class="flex-1 py-3 text-sm font-bold text-[#1F2937] border-b-2 border-[#1F2937] transition-all" id="tab-pending" onclick="switchTab('pending')">
|
||||
待出库 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5">3</span>
|
||||
</button>
|
||||
<button class="flex-1 py-3 text-sm font-bold text-gray-400 border-b-2 border-transparent transition-all" id="tab-shipped" onclick="switchTab('shipped')">
|
||||
已出库 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5">3</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 列表内容 -->
|
||||
<div class="p-3 pt-4 space-y-3" id="list-pending">
|
||||
<!-- 卡片 1 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">78133363573</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">待出库</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 提货车辆行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">提货车辆:皖A88909</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">代理人</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">ATR</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">100件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">200kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 库位信息行 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">库位:A区-03货位</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363573', status: '待出库', agent: 'ATR', flightDate: '20260706', flightNo: 'MU1111', pieces: '100', weight: '200', goodsName: '电子元器件', carrier: 'MU', origin: 'LAX', location: 'A区-03货位', code: 'NOR', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 2 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">78133363574</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">待出库</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 提货车辆行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">提货车辆:皖A78223</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">代理人</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">CTA</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">85件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">150kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 库位信息行 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">库位:B区-05货位</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363574', status: '待出库', agent: 'CTA', flightDate: '20260706', flightNo: 'CA2222', pieces: '85', weight: '150', goodsName: '服装鞋帽', carrier: 'CA', origin: 'PEK', location: 'B区-05货位', code: 'ATR', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 3 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">78133363575</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">待出库</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 提货车辆行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">提货车辆:皖A67651</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">代理人</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">DHL</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">200件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">450kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 库位信息行 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">库位:C区-01货位</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363575', status: '待出库', agent: 'DHL', flightDate: '20260707', flightNo: '3U3333', pieces: '200', weight: '450', goodsName: '精密模具', carrier: '3U', origin: 'CTU', location: 'C区-01货位', code: 'ATR', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 已出库列表内容 -->
|
||||
<div class="p-3 pt-4 space-y-3 hidden" id="list-shipped">
|
||||
<!-- 卡片 1 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">78133363001</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已出库</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作信息行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">提货车辆:皖A23422</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">代理人</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">ATR</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">50件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">80kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作人信息 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出库人:张三 · 出库时间:2026-07-05 11:45</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer" onclick="showDetail({id: '78133363001', status: '已出库', agent: 'ATR', flightDate: '20260704', flightNo: 'MU1111', pieces: '50', weight: '80', pickupNo: 'TH20260701001', goodsName: '精密仪器', carrier: 'MU', origin: 'LAX', location: 'B区-05货位', code: 'ATR', counterTime: '2026-07-05 10:30', counterPerson: '张三', outboundTime: '2026-07-05 11:45', outboundPerson: '张三'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 2 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">78133363002</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已出库</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作信息行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">提货车辆:皖A79909</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">代理人</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">CTA</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">120件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">300kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作人信息 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出库人:赵六 · 出库时间:2026-07-06 10:30</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer" onclick="showDetail({id: '78133363002', status: '已出库', agent: 'CTA', flightDate: '20260705', flightNo: 'CA2222', pieces: '120', weight: '300', pickupNo: 'TH20260702002', goodsName: '鲜花绿植', carrier: 'CA', origin: 'SFO', location: 'A区-08货位', code: 'ATR', counterTime: '2026-07-06 09:15', counterPerson: '王五', outboundTime: '2026-07-06 10:30', outboundPerson: '赵六'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 3 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3">
|
||||
<!-- 顶部行 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">78133363003</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已出库</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作信息行 -->
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:airplane"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">提货车辆:皖A66712</span>
|
||||
</div>
|
||||
<!-- 统计框 -->
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">代理人</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">DHL</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">75件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">160kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作人信息 -->
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出库人:孙八 · 出库时间:2026-07-06 15:20</span>
|
||||
</div>
|
||||
<!-- 查看详情链接 -->
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer" onclick="showDetail({id: '78133363003', status: '已出库', agent: 'DHL', flightDate: '20260705', flightNo: '3U3333', pieces: '75', weight: '160', pickupNo: 'TH20260703003', goodsName: '生鲜食品', carrier: '3U', origin: 'CDG', location: 'D区-02货位', code: 'NOR', counterTime: '2026-07-06 14:00', counterPerson: '钱七', outboundTime: '2026-07-06 15:20', outboundPerson: '孙八'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部工具栏 -->
|
||||
<div class="absolute bottom-0 w-full bg-white border-t border-gray-200 z-40 flex flex-col pt-4 pb-4" id="bottom-toolbar">
|
||||
<!-- 操作区域 -->
|
||||
<div class="px-4 flex items-center w-full">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="custom-checkbox-round" id="select-all-cb" onclick="toggleSelectAll(this.checked)" type="checkbox"/>
|
||||
<label class="text-[13px] text-[#1F2937] font-medium cursor-pointer" for="select-all-cb">全选</label>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-[12px] text-[#9CA3AF]">已选 <span id="selected-count">0</span> 项</span>
|
||||
<button class="bg-[#2563EB] text-white text-[15px] font-bold px-5 py-2 rounded-[10px] shadow-lg shadow-blue-500/25 btn-active" onclick="alert('出库操作')">
|
||||
出库
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 页面2: 详情页 -->
|
||||
<div class="page" id="page-detail">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="bg-[#2563EB] text-white h-12 flex items-center justify-between px-4 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="switchPage('home')">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-bold -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium">运单详情</h1>
|
||||
<div class="w-16"></div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar pb-8 p-4">
|
||||
<!-- 货运单据样式卡片 -->
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-hidden border border-gray-100">
|
||||
<!-- 1. 顶部蓝色条带 -->
|
||||
<div class="bg-[#2563EB] px-4 py-3 flex justify-between items-center text-white">
|
||||
<span class="font-bold text-[17px] leading-tight" id="detail-awb">781-22222620</span>
|
||||
<div class="flex items-center text-[13px] font-normal opacity-90">
|
||||
<span class="w-2 h-2 bg-green-400 rounded-full mr-1.5"></span>
|
||||
<span id="detail-time">2026-05-10 08:30</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<!-- 2. 标签区 -->
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-[#EF4444] text-white text-[11px] px-1.5 py-0.5 rounded leading-tight font-medium">国际进港</span>
|
||||
<span class="text-[13px] font-normal text-[#666666] border-l border-gray-300 pl-2">普通货物运输</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 3. 货物名称 -->
|
||||
<div class="text-[22px] font-bold text-[#111827] mb-3" id="detail-goods-name">电子元器件</div>
|
||||
<!-- 4. 两列信息表格 -->
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-3">
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">件数</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-count">55</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">重量</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-weight">622.00 kg</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">特码</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-code">NOR</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">代理</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-agent">ATR</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">始发港</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-origin">LAX</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">目的港</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-carrier">MU</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">航班日期</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-flight-date">20260706</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">航班号</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-flight-no">MU1111</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">库位</div>
|
||||
<div class="flex">
|
||||
<span class="bg-orange-50 text-orange-600 px-1 rounded text-[15px] font-bold" id="detail-location">1111111</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">提货单号</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-delivery-no">TH20260706001</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 提货信息卡片 -->
|
||||
<div class="mt-4 bg-white rounded-xl shadow-sm overflow-hidden border border-gray-200 hidden" id="detail-outbound-card">
|
||||
<div class="bg-[#10B981] px-4 py-2.5">
|
||||
<span class="text-white font-semibold text-[15px]">提货信息</span>
|
||||
</div>
|
||||
<div class="bg-white px-4 py-3 grid grid-cols-2 gap-y-3">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">柜台办理人</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-counter-person">-</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">柜台办理时间</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-counter-time">-</span>
|
||||
</div>
|
||||
<div class="flex flex-col" id="detail-outbound-person-row">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">出库人</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-outbound-person">-</span>
|
||||
</div>
|
||||
<div class="flex flex-col" id="detail-outbound-time-row">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">出库时间</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-outbound-time">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部筛选面板蒙层 -->
|
||||
<div class="overlay absolute inset-0 z-40" id="filter-overlay" onclick="toggleFilterDrawer(false)"></div>
|
||||
<!-- 筛选面板 -->
|
||||
<div class="absolute bottom-0 w-full bg-white rounded-t-3xl z-50 p-6 shadow-2xl overflow-hidden" id="filter-panel">
|
||||
<!-- 标题 + 关闭按钮 -->
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<h2 class="text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="toggleFilterDrawer(false)">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- 航班日期 -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班日期</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择航班日期</span>
|
||||
<iconify-icon class="text-gray-400" icon="mdi:calendar-month-outline"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 航班号 -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班号</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between">
|
||||
<input class="bg-transparent outline-none text-sm w-full text-gray-800" placeholder="请输入航班号" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 代理人 -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">代理人</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择代理人</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 特码 -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">特码</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择特码</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 pt-2">
|
||||
<button class="flex-1 bg-white border border-gray-300 text-gray-600 py-3.5 rounded-xl font-bold btn-active" onclick="alert('已重置')">
|
||||
重置
|
||||
</button>
|
||||
<button class="flex-1 bg-[#2563EB] text-white py-3.5 rounded-xl font-bold shadow-lg shadow-blue-500/20 btn-active" onclick="toggleFilterDrawer(false)">
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// 页面切换逻辑
|
||||
function switchPage(pageId) {
|
||||
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
||||
document.getElementById('page-' + pageId).classList.add('active');
|
||||
|
||||
// 如果进入详情页,可以重置滚动
|
||||
if(pageId === 'detail') {
|
||||
document.querySelector('#page-detail .overflow-y-auto').scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 子Tab切换逻辑
|
||||
function switchTab(type) {
|
||||
const tabPending = document.getElementById('tab-pending');
|
||||
const tabShipped = document.getElementById('tab-shipped');
|
||||
const listPending = document.getElementById('list-pending');
|
||||
const listShipped = document.getElementById('list-shipped');
|
||||
const bottomToolbar = document.getElementById('bottom-toolbar');
|
||||
|
||||
if (type === 'pending') {
|
||||
tabPending.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabPending.classList.remove('text-gray-400', 'border-transparent');
|
||||
tabShipped.classList.add('text-gray-400', 'border-transparent');
|
||||
tabShipped.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
|
||||
listPending.classList.remove('hidden');
|
||||
listShipped.classList.add('hidden');
|
||||
bottomToolbar.style.display = 'flex';
|
||||
} else {
|
||||
tabShipped.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabShipped.classList.remove('text-gray-400', 'border-transparent');
|
||||
tabPending.classList.add('text-gray-400', 'border-transparent');
|
||||
tabPending.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
|
||||
listShipped.classList.remove('hidden');
|
||||
listPending.classList.add('hidden');
|
||||
bottomToolbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 全选/取消全选
|
||||
function toggleSelectAll(checked) {
|
||||
const checkboxes = document.querySelectorAll('.item-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = checked;
|
||||
updateCardStyle(cb);
|
||||
});
|
||||
updateSelectionCount();
|
||||
}
|
||||
|
||||
// 处理卡片复选框点击
|
||||
function handleCheckboxClick(checkbox, event) {
|
||||
event.stopPropagation(); // 阻止触发卡片详情点击
|
||||
updateCardStyle(checkbox);
|
||||
updateSelectionCount();
|
||||
|
||||
// 更新全选状态
|
||||
const checkboxes = document.querySelectorAll('.item-checkbox');
|
||||
const selectAllCb = document.getElementById('select-all-cb');
|
||||
const allChecked = Array.from(checkboxes).every(cb => cb.checked);
|
||||
selectAllCb.checked = allChecked;
|
||||
}
|
||||
|
||||
// 更新卡片选中样式 (已根据需求移除背景和边框变化,仅保留复选框状态)
|
||||
function updateCardStyle(checkbox) {
|
||||
// 卡片不再随选中状态改变背景和边框颜色
|
||||
}
|
||||
|
||||
// 更新选中计数
|
||||
function updateSelectionCount() {
|
||||
const checkedCount = document.querySelectorAll('.item-checkbox:checked').length;
|
||||
document.getElementById('selected-count').textContent = checkedCount;
|
||||
}
|
||||
|
||||
// 显示详情页并填充数据
|
||||
function showDetail(data) {
|
||||
if (!data) return;
|
||||
|
||||
const setText = (id, text) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.textContent = text || '-';
|
||||
};
|
||||
|
||||
setText('detail-awb', data.id);
|
||||
setText('detail-time', new Date().toISOString().replace('T', ' ').substring(0, 16));
|
||||
setText('detail-goods-name', data.goodsName);
|
||||
setText('detail-count', data.pieces);
|
||||
setText('detail-weight', data.weight + (data.weight && data.weight.toString().includes('kg') ? '' : ' kg'));
|
||||
setText('detail-code', data.code);
|
||||
setText('detail-agent', data.agent);
|
||||
setText('detail-carrier', data.carrier);
|
||||
setText('detail-origin', data.origin);
|
||||
setText('detail-flight-date', data.flightDate);
|
||||
setText('detail-flight-no', data.flightNo);
|
||||
setText('detail-location', data.location);
|
||||
setText('detail-delivery-no', data.pickupNo);
|
||||
|
||||
const outboundCard = document.getElementById('detail-outbound-card');
|
||||
if (outboundCard) {
|
||||
// 始终显示提货信息卡片
|
||||
outboundCard.classList.remove('hidden');
|
||||
|
||||
setText('detail-counter-person', data.counterPerson);
|
||||
setText('detail-counter-time', data.counterTime);
|
||||
setText('detail-outbound-person', data.outboundPerson);
|
||||
setText('detail-outbound-time', data.outboundTime);
|
||||
|
||||
const outboundPersonRow = document.getElementById('detail-outbound-person-row');
|
||||
const outboundTimeRow = document.getElementById('detail-outbound-time-row');
|
||||
|
||||
if (data.status === '已出库') {
|
||||
// 已出库显示全部
|
||||
if (outboundPersonRow) outboundPersonRow.classList.remove('hidden');
|
||||
if (outboundTimeRow) outboundTimeRow.classList.remove('hidden');
|
||||
} else {
|
||||
// 待出库隐藏出库人信息
|
||||
if (outboundPersonRow) outboundPersonRow.classList.add('hidden');
|
||||
if (outboundTimeRow) outboundTimeRow.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
switchPage('detail');
|
||||
}
|
||||
|
||||
// 筛选面板显示隐藏
|
||||
function toggleFilterDrawer(show) {
|
||||
const panel = document.getElementById('filter-panel');
|
||||
const overlay = document.getElementById('filter-overlay');
|
||||
if (show) {
|
||||
panel.classList.add('show');
|
||||
overlay.classList.add('show');
|
||||
} else {
|
||||
panel.classList.remove('show');
|
||||
overlay.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化显示日期
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
console.log("System date initialized:", today);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
147
documents/5.5寸手持端设计文件/10_进港查询/运单追踪.html
Normal file
147
documents/5.5寸手持端设计文件/10_进港查询/运单追踪.html
Normal file
@@ -0,0 +1,147 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>国际进港查询 - 物流跟踪</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
src: local('sans-serif');
|
||||
}
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.no-scrollbar::-webkit-scrollbar { display: none; }
|
||||
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
|
||||
/* 模拟手机容器阴影 */
|
||||
.phone-shell {
|
||||
box-shadow: 0 50px 100px -20px rgba(0,0,0,0.25), 0 30px 60px -30px rgba(0,0,0,0.3);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-200 flex justify-center items-center min-h-screen p-4">
|
||||
<!-- 模拟手机屏幕容器 -->
|
||||
<div class="phone-shell w-[375px] h-[812px] bg-[#F5F5F7] overflow-hidden flex flex-col relative rounded-[48px] border-[10px] border-gray-900">
|
||||
<!-- 顶部蓝色区域 (包含状态栏和导航栏) -->
|
||||
<div class="bg-[#1A73E8] flex-none">
|
||||
<!-- 系统状态栏模拟 -->
|
||||
<div class="h-11 w-full flex justify-between items-center px-8 text-white">
|
||||
<span class="text-[15px] font-bold">09:41</span>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
<iconify-icon class="text-sm" icon="mdi:signal-cellular-3"></iconify-icon>
|
||||
<iconify-icon class="text-base" icon="mdi:wifi"></iconify-icon>
|
||||
<iconify-icon class="text-xl" icon="mdi:battery"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导航栏 -->
|
||||
<div class="h-[44px] px-4 flex items-center justify-between">
|
||||
<button class="flex items-center text-white space-x-0.5">
|
||||
<iconify-icon class="text-2xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span class="text-base">返回</span>
|
||||
</button>
|
||||
<h1 class="text-lg font-bold text-white absolute left-1/2 -translate-x-1/2">运单追踪</h1>
|
||||
<div class="w-10"></div> <!-- 占位保持平衡 -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- 可滚动内容区 -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar px-5 pt-6 pb-12">
|
||||
<!-- 流转状态卡片 -->
|
||||
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100/50 mb-5">
|
||||
<!-- 运单号 Header -->
|
||||
<div class="flex items-center justify-between pt-2 pb-5 mb-8 border-b border-gray-100">
|
||||
<div class="text-xl font-black text-gray-900 leading-none tracking-tight">
|
||||
78133358743
|
||||
</div>
|
||||
<span class="text-[11px] font-medium text-blue-600 bg-blue-50 px-2.5 py-1 rounded-full border border-blue-100">国际进港</span>
|
||||
</div>
|
||||
<!-- 流转状态标题 -->
|
||||
<div class="mb-6">
|
||||
<h2 class="text-base font-semibold text-gray-700 flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-gray-700 inline-block" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
流转状态
|
||||
</h2>
|
||||
</div>
|
||||
<div class="relative pl-7 pb-2">
|
||||
<div class="absolute left-[9px] top-2 bottom-4 w-[2px] bg-gray-100"></div>
|
||||
|
||||
<!-- 状态 1: 当前 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[27px] top-1 w-[20px] h-[20px] rounded-full bg-green-600 border-4 border-green-100 flex items-center justify-center ring-1 ring-green-600/20">
|
||||
<div class="w-2 h-2 rounded-full bg-white"></div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-green-700">原始舱单</span>
|
||||
<span class="text-green-600 text-xs font-normal bg-green-50 px-2 py-0.5 rounded-full">放行状态:01</span>
|
||||
</div>
|
||||
<div class="text-gray-400 text-xs font-normal flex items-center gap-1.5">
|
||||
<span>2026-07-10 11:03:38</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 2 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">航班落地</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 2 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">分拣理货</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 3 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">理货申报</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 4 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">海关放行</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 5 -->
|
||||
<div class="relative mb-10">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">柜台办结</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 状态 6 -->
|
||||
<div class="relative mb-2">
|
||||
<div class="absolute -left-[22px] top-1.5 w-[10px] h-[10px] rounded-full bg-gray-200 border-2 border-white"></div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold text-gray-400">提取出库</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 模块 4: 操作详情卡片 -> 已删除 -->
|
||||
<!-- 底部占位 -->
|
||||
<div class="text-center py-6">
|
||||
<p class="text-[10px] text-gray-300 uppercase tracking-[0.2em] font-bold">查询信息已触底</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部 Home Indicator 模拟 -->
|
||||
<div class="h-8 w-full flex justify-center items-center flex-none bg-white/60 backdrop-blur-md">
|
||||
<div class="w-32 h-1.5 bg-black rounded-full opacity-20"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>ml>
|
||||
951
documents/5.5寸手持端设计文件/10_进港查询/进港查询.html
Normal file
951
documents/5.5寸手持端设计文件/10_进港查询/进港查询.html
Normal file
@@ -0,0 +1,951 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>进港查询</title>
|
||||
<!-- Tailwind CSS (本地) -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<!-- Iconify 图标 -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'PingFang SC';
|
||||
src: local('PingFang SC');
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #e5e7eb;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 隐藏滚动条但保留功能 */
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.hide-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.tag-occupied { background-color: #dcfce7; color: #22c55e; } /* 已出库 - 绿色 */
|
||||
.tag-available { background-color: #dcfce7; color: #22c55e; } /* 已出库 - 绿色 */
|
||||
.tag-repair { background-color: #fce7e7; color: #ef4444; } /* 离港 - 红色 */
|
||||
|
||||
/* 页面切换动画 */
|
||||
.page {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
background-color: #f3f4f6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.page::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.page-hidden-right { transform: translateX(100%); }
|
||||
.page-hidden-left { transform: translateX(-100%); }
|
||||
|
||||
|
||||
/* 统一的深蓝色调 */
|
||||
.bg-header-blue { background-color: #2563EB; }
|
||||
|
||||
|
||||
/* 标签页样式 */
|
||||
.tab-item {
|
||||
position: relative;
|
||||
padding: 12px 0;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
.tab-item.active {
|
||||
color: #2563eb;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tab-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background-color: #2563eb;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.tab-item.active::after {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
/* ========== 筛选弹窗样式 提升层级+动画稳定 ========== */
|
||||
.filter-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.25s ease, visibility 0.25s ease;
|
||||
}
|
||||
.filter-overlay.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.filter-sheet {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #fff;
|
||||
border-top-left-radius: 20px;
|
||||
border-top-right-radius: 20px;
|
||||
padding: 20px;
|
||||
z-index: 10000;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.25s ease-out;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.filter-sheet.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
/* 筛选输入框统一样式 */
|
||||
.filter-input {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.filter-input:focus {
|
||||
border-color: #2563EB;
|
||||
}
|
||||
|
||||
/* 下拉框箭头浅灰色 + 全浏览器兼容修复 */
|
||||
select.filter-input {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%239ca3af' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10l-5 5z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 12px center;
|
||||
background-size: 16px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
/* 清除IE默认下拉箭头 */
|
||||
select.filter-input::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="w-[375px] h-[812px] bg-[#F3F4F6] overflow-hidden flex flex-col relative shadow-2xl rounded-[40px] border-[8px] border-gray-800">
|
||||
<!-- 状态栏 -->
|
||||
<div class="bg-header-blue flex-none z-50">
|
||||
<div class="flex justify-between items-center px-6 pt-3 pb-1 text-[12px] font-medium text-white">
|
||||
<span>09:41</span>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
<iconify-icon icon="material-symbols:signal-cellular-4-bar-rounded"></iconify-icon>
|
||||
<iconify-icon icon="material-symbols:wifi"></iconify-icon>
|
||||
<iconify-icon icon="material-symbols:battery-full-rounded"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选弹窗容器 移到最外层,层级提升 -->
|
||||
<div class="filter-overlay" id="filterOverlay"></div>
|
||||
<div class="filter-sheet" id="filterSheet">
|
||||
<!-- 顶部标题栏 -->
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="closeFilter()">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
<h2 class="text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
</div>
|
||||
<!-- 筛选表单区域 -->
|
||||
<div class="space-y-4 mb-7">
|
||||
<!-- 1.航班日期 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班日期起</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择航班日期" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 1.航班日期 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班日期止</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择航班日期" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 2.航班号 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班号</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入航班号" type="text" id="filterFlightNo">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 3.代理 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">代理</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择代理" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 4.特码 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">特码</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择特码" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 5.始发站 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">始发站</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入始发站" type="text" id="filterOrigin">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 6.运单类型 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">运单类型</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择运单类型" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 7.业务类型 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">业务类型</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择业务类型" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:chevron-down" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 8.品名(中) -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">品名(中)</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入中文品名" type="text" id="filterCnName">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部操作按钮 -->
|
||||
<div class="flex gap-3">
|
||||
<button class="flex-1 h-11 bg-gray-100 text-gray-600 rounded-lg text-sm font-medium" onclick="resetFilter()">重置</button>
|
||||
<button class="flex-1 h-11 bg-[#2563EB] text-white rounded-lg text-sm font-medium" onclick="confirmFilter()">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面主容器 -->
|
||||
<div class="flex-1 relative overflow-hidden">
|
||||
<!-- 列表页 -->
|
||||
<div class="page z-10" id="listPage">
|
||||
<!-- 头部 -->
|
||||
<header class="bg-header-blue pt-2 pb-4 px-4 flex items-center relative flex-none">
|
||||
<button class="flex items-center text-white text-sm absolute left-4">
|
||||
<iconify-icon class="text-xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="mx-auto text-white font-bold text-lg">进港查询</h1>
|
||||
</header>
|
||||
<!-- 搜索栏 筛选按钮已移除边框 -->
|
||||
<div class="p-3 bg-transparent flex items-center gap-3 border-b border-gray-100 flex-none">
|
||||
<div class="flex-1 bg-white rounded-xl flex items-center px-3 py-[10px] gap-2 border border-gray-50">
|
||||
<iconify-icon class="text-[#9ca3af] text-lg" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="bg-transparent text-[14px] w-full outline-none text-gray-700 placeholder-[#9ca3af]" placeholder="搜索运单号" type="text"/>
|
||||
<iconify-icon class="text-[#2563eb] text-lg" icon="mdi:qrcode-scan" onclick="alert('开启扫码功能')"></iconify-icon>
|
||||
</div>
|
||||
<!-- 筛选按钮 无圆形实线边框 -->
|
||||
<button id="filterBtn" class="w-10 h-10 flex items-center justify-center bg-white rounded-full relative z-[99999]">
|
||||
<iconify-icon class="text-[#2563eb] text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 统计标签 -->
|
||||
<div class="flex items-center gap-2 px-4 pb-3">
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#2563EB]">
|
||||
<div class="text-[11px] text-gray-500">总票数</div>
|
||||
<div class="text-[22px] font-bold text-[#2563EB] leading-tight mt-0.5">4</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-orange-500">
|
||||
<div class="text-[11px] text-gray-500">已入库</div>
|
||||
<div class="text-[22px] font-bold text-orange-500 leading-tight mt-0.5">2</div>
|
||||
</div>
|
||||
<div class="flex-1 bg-white rounded-lg px-3 py-2.5 shadow-sm border-l-[4px] border-l-[#10B981]">
|
||||
<div class="text-[11px] text-gray-500">已出库</div>
|
||||
<div class="text-[22px] font-bold text-[#10B981] leading-tight mt-0.5">2</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ULD 列表内容 -->
|
||||
<div class="p-3 space-y-3 pb-24 overflow-y-auto hide-scrollbar">
|
||||
<!-- 卡片 1 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363396')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363396</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已出库</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:444</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:555</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:DGR</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 2 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363397')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363397</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已出库</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:120</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:320</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:徽远</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:PER</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 3 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363398')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363398</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已入库</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:268</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:780</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:NOR</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 4 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363399')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363399</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已出库</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:85</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:210</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:徽远</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:DGR</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片 5 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm active:bg-gray-50 transition-colors border border-gray-50 cursor-pointer" onclick="showDetail('78133363400')">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-blue-500"></div>
|
||||
<span class="font-bold text-[#1a1a1a]">78133363400</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-green-100 text-green-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">已出库</span>
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:chevron-right"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-y-2 text-[13px] text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:package-variant-closed"></iconify-icon>
|
||||
<span>件数:350</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:weight-kilogram"></iconify-icon>
|
||||
<span>重量:430</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-tie"></iconify-icon>
|
||||
<span>代理人:中外运</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:tag-outline"></iconify-icon>
|
||||
<span>特码:PER</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详情页 -->
|
||||
<div class="page z-20 page-hidden-right !bg-[#f0f2f5]" id="detailPage">
|
||||
<!-- 头部 -->
|
||||
<header class="bg-header-blue pt-2 pb-4 px-4 flex items-center relative flex-none">
|
||||
<button class="flex items-center text-white text-sm absolute left-4 active:opacity-70" onclick="goBack()">
|
||||
<iconify-icon class="text-xl" icon="mdi:chevron-left"></iconify-icon>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<h1 class="mx-auto text-white font-bold text-lg">进港货物</h1>
|
||||
<div class="absolute right-4 flex items-center">
|
||||
<iconify-icon class="text-red-500 text-2xl" icon="mdi:file-document-edit-outline"></iconify-icon>
|
||||
</div>
|
||||
</header>
|
||||
<!-- 标签页 -->
|
||||
<div class="bg-white px-4 border-b border-gray-200 flex-none">
|
||||
<div class="flex justify-around">
|
||||
<div class="tab-item active cursor-pointer" onclick="switchTab(0)">运单信息</div>
|
||||
<div class="tab-item cursor-pointer" onclick="switchTab(1)">仓库信息</div>
|
||||
<div class="tab-item cursor-pointer" onclick="switchTab(2)">库位信息</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 详情内容区 -->
|
||||
<div class="flex-1 overflow-y-auto hide-scrollbar">
|
||||
<!-- 运单信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3" id="tab-content-0">
|
||||
<!-- 卡片1:基本信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800" id="det-uldTitle">基本信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-2 gap-2">
|
||||
<!-- Row 1 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-billNo"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">代理人</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-agent"></span>
|
||||
</div>
|
||||
<!-- Row 2 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">特码</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-code"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">承运人</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-carrier"></span>
|
||||
</div>
|
||||
<!-- Row 3 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">始发港</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-route"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">中转站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-outboundTime"></span>
|
||||
</div>
|
||||
<!-- Row 4 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">目的站</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-dest"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">UN编号</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-unNo"></span>
|
||||
</div>
|
||||
<!-- Row 5 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center" id="det-lockStatus-container">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5" id="det-lockStatus-label">锁定状态</span>
|
||||
<div id="det-lockStatus-wrapper">
|
||||
<span class="text-xs font-semibold" id="det-lockStatus"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">包装类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-packType"></span>
|
||||
</div>
|
||||
<!-- Row 6 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">业务类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-type"></span>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">出库时间</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-outboundTime"></span>
|
||||
</div>
|
||||
<!-- Row 7 运单类型单独一行 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单类型</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-billType"></span>
|
||||
</div>
|
||||
<!-- Row 8 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">品名(中)</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-itemNameCn"></span>
|
||||
</div>
|
||||
<!-- Row 9 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">品名(英)</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-itemNameEn"></span>
|
||||
</div>
|
||||
<!-- Row 10 -->
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center col-span-2">
|
||||
<span class="text-[10px] text-gray-500 text-[14px] mb-0.5">备注</span>
|
||||
<span class="text-xs text-gray-800 font-semibold truncate" id="det-remark"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片2:件重信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800">件重信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-3 gap-2">
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单件数</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-pieces"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">运单重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-weight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">计费重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-calcWeight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">已出库件数</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-inPieces"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">已出库重量</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-inWeight"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center opacity-0">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">—</div>
|
||||
<div class="text-xs text-gray-800 font-semibold">—</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片3:回执信息 -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-4 py-2.5 flex items-center border-b border-gray-100">
|
||||
<div class="w-1 h-4 bg-blue-500 rounded-full inline-block mr-2"></div>
|
||||
<span class="text-xs font-semibold text-gray-800">回执信息</span>
|
||||
</div>
|
||||
<div class="p-3 grid grid-cols-3 gap-2">
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">原始舱单</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-originalManifest"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">理货报告</div>
|
||||
<div class="text-xs text-gray-800 font-semibold" id="det-tallyReport"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50/30 rounded-lg p-2.5 flex flex-col justify-center" id="det-customsRelease-container">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">海关放行</div>
|
||||
<div id="det-customsRelease-wrapper">
|
||||
<div class="text-xs font-semibold" id="det-customsRelease"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 仓库信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3 hidden" id="tab-content-1"></div>
|
||||
<!-- 库位信息标签页 -->
|
||||
<div class="tab-pane p-3 space-y-3 hidden" id="tab-content-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const uldData = {
|
||||
'78133363396': {
|
||||
billNo: '78133363396', agent: '中外运', code: 'DGR',
|
||||
pieces: '444', weight: '555.0', type: '普通货物运输',
|
||||
inPieces: '444', inWeight: '555.0', calcWeight: '555.0',
|
||||
carrier: 'CA', route: 'LAX', packType: '纸箱',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'HFE',
|
||||
itemNameCn: '电子元器件', itemNameEn: 'Electronic Components',
|
||||
lockStatus: '未锁定', outboundTime: '—', remark: '需冷藏存储',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '1', pieces: 200, weight: 300.0, flightDate: '2026-07-08', flightNo: 'CA1234',route: 'LAX',dest: 'HFE', time: '2026-07-08 14:30:00' },
|
||||
{ batch: '2', pieces: 244, weight: 255.0, flightDate: '2026-07-09', flightNo: 'CA5678',route: 'LAX',dest: 'HFE', time: '2026-07-09 09:10:46' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'A-01-001', inPerson: '张三', inTime: '2026-07-08 14:30', outPerson: '李四', outTime: '2026-07-09 10:00' },
|
||||
{ id: 'B-02-005', inPerson: '王五', inTime: '2026-07-09 09:10', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363397': {
|
||||
billNo: '78133363397', agent: '徽远', code: 'PER',
|
||||
pieces: '120', weight: '320.0', type: '普通货物运输',
|
||||
inPieces: '120', inWeight: '320.0', calcWeight: '320.0',
|
||||
carrier: 'MU', route: 'PVG-NRT', packType: '托盘',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'PVG',
|
||||
itemNameCn: '服装', itemNameEn: 'Garments',
|
||||
lockStatus: '未锁定', outboundTime: '—', remark: '易碎品,轻拿轻放',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '3', pieces: 60, weight: 150.0, flightDate: '2026-07-07', flightNo: 'MU123',route: 'LAX',dest: 'HFE', time: '2026-07-07 16:20:00' },
|
||||
{ batch: '4', pieces: 60, weight: 170.0, flightDate: '2026-07-08', flightNo: 'MU456',route: 'LAX',dest: 'HFE', time: '2026-07-08 11:00:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'A-03-012', inPerson: '赵六', inTime: '2026-07-07 16:20', outPerson: '钱七', outTime: '2026-07-08 14:00' },
|
||||
{ id: 'C-01-008', inPerson: '孙八', inTime: '2026-07-08 11:00', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363398': {
|
||||
billNo: '78133363398', agent: '中外运', code: 'NOR',
|
||||
pieces: '268', weight: '780.0', type: '普通货物运输',
|
||||
inPieces: '268', inWeight: '780.0', calcWeight: '780.0',
|
||||
carrier: 'CZ', route: 'CAN-AMS', packType: '托盘',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'CAN',
|
||||
itemNameCn: '机械设备', itemNameEn: 'Machinery Equipment',
|
||||
lockStatus: '锁定', outboundTime: '2026-07-09 08:00', remark: '需叉车装卸',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '5', pieces: 150, weight: 420.0, flightDate: '2026-07-08', flightNo: 'CZ789',route: 'LAX',dest: 'HFE', time: '2026-07-08 22:15:00' },
|
||||
{ batch: '6', pieces: 118, weight: 360.0, flightDate: '2026-07-09', flightNo: 'CZ012',route: 'LAX',dest: 'HFE', time: '2026-07-09 06:30:00' },
|
||||
{ batch: '7', pieces: 100, weight: 200.0, flightDate: '2026-07-09', flightNo: 'CZ345',route: 'LAX',dest: 'HFE', time: '2026-07-09 13:00:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'B-01-003', inPerson: '张三', inTime: '2026-07-08 22:15', outPerson: '李四', outTime: '2026-07-09 06:00' },
|
||||
{ id: 'A-02-010', inPerson: '王五', inTime: '2026-07-09 06:30', outPerson: '—', outTime: '—' },
|
||||
{ id: 'C-03-007', inPerson: '赵六', inTime: '2026-07-09 13:00', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363399': {
|
||||
billNo: '78133363399', agent: '徽远', code: 'DGR',
|
||||
pieces: '85', weight: '210.0', type: '危险品运输',
|
||||
inPieces: '85', inWeight: '210.0', calcWeight: '210.0',
|
||||
carrier: '3U', route: 'CTU-SIN', packType: '铁箱',
|
||||
billType: '国际进港(国际直航)', unNo: 'UN3480', dest: 'CTU',
|
||||
itemNameCn: '锂电池', itemNameEn: 'Lithium Battery',
|
||||
lockStatus: '锁定', outboundTime: '—', remark: '危险品,单独存放',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '8', pieces: 45, weight: 110.0, flightDate: '2026-07-08', flightNo: '3U678',route: 'LAX',dest: 'HFE', time: '2026-07-08 18:00:00' },
|
||||
{ batch: '9', pieces: 40, weight: 100.0, flightDate: '2026-07-09', flightNo: '3U901',route: 'LAX',dest: 'HFE', time: '2026-07-09 07:45:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'D-01-002', inPerson: '钱七', inTime: '2026-07-08 18:00', outPerson: '孙八', outTime: '2026-07-09 08:00' },
|
||||
{ id: 'A-04-015', inPerson: '张三', inTime: '2026-07-09 07:45', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
},
|
||||
'78133363400': {
|
||||
billNo: '78133363400', agent: '中外运', code: 'PER',
|
||||
pieces: '350', weight: '430.0', type: '普通货物运输',
|
||||
inPieces: '350', inWeight: '430.0', calcWeight: '430.0',
|
||||
carrier: 'HU', route: 'HKG-HFE', packType: '纸箱',
|
||||
billType: '国际进港(国际直航)', unNo: '—', dest: 'HFE',
|
||||
itemNameCn: '食品', itemNameEn: 'Food Products',
|
||||
lockStatus: '未锁定', outboundTime: '2026-07-09 11:30', remark: '保质期至2026-12',
|
||||
originalManifest: '01', tallyReport: '01', customsRelease: '11',
|
||||
warehouse: [
|
||||
{ batch: '10', pieces: 180, weight: 220.0, flightDate: '2026-07-07', flightNo: 'HU234',route: 'LAX',dest: 'HFE', time: '2026-07-07 20:00:00' },
|
||||
{ batch: '11', pieces: 170, weight: 210.0, flightDate: '2026-07-09', flightNo: 'HU567',route: 'LAX',dest: 'HFE', time: '2026-07-09 10:30:00' }
|
||||
],
|
||||
locations: [
|
||||
{ id: 'B-02-011', inPerson: '李四', inTime: '2026-07-07 20:00', outPerson: '王五', outTime: '2026-07-08 16:00' },
|
||||
{ id: 'C-01-009', inPerson: '赵六', inTime: '2026-07-09 10:30', outPerson: '—', outTime: '—' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// 核心修复:等待DOM全部渲染完成后再绑定点击事件,避免获取元素为null
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
const filterBtn = document.getElementById('filterBtn');
|
||||
|
||||
// 点击遮罩空白关闭,添加self防止弹窗内部点击穿透关闭
|
||||
filterOverlay.addEventListener('click', function(e) {
|
||||
if (e.target === filterOverlay) {
|
||||
closeFilter();
|
||||
}
|
||||
});
|
||||
// 筛选按钮点击打开弹窗
|
||||
filterBtn.addEventListener('click', openFilter);
|
||||
});
|
||||
|
||||
// 打开筛选弹窗
|
||||
function openFilter() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
filterOverlay.classList.add('show');
|
||||
setTimeout(() => filterSheet.classList.add('show'), 10);
|
||||
}
|
||||
|
||||
// 关闭筛选弹窗
|
||||
function closeFilter() {
|
||||
const filterOverlay = document.getElementById('filterOverlay');
|
||||
const filterSheet = document.getElementById('filterSheet');
|
||||
filterSheet.classList.remove('show');
|
||||
setTimeout(() => filterOverlay.classList.remove('show'), 250);
|
||||
}
|
||||
|
||||
// 重置所有筛选输入框
|
||||
function resetFilter() {
|
||||
document.querySelectorAll('.filter-input').forEach(input => {
|
||||
input.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
// 确定筛选(模拟查询,可对接后端筛选逻辑)
|
||||
function confirmFilter() {
|
||||
const params = {
|
||||
flightDate: document.getElementById('filterFlightDate').value,
|
||||
flightNo: document.getElementById('filterFlightNo').value,
|
||||
agent: document.getElementById('filterAgent').value,
|
||||
code: document.getElementById('filterCode').value,
|
||||
origin: document.getElementById('filterOrigin').value,
|
||||
billType: document.getElementById('filterBillType').value,
|
||||
bizType: document.getElementById('filterBizType').value,
|
||||
cnName: document.getElementById('filterCnName').value
|
||||
};
|
||||
console.log('筛选参数:', params);
|
||||
alert('筛选执行成功,控制台可查看筛选条件');
|
||||
closeFilter();
|
||||
}
|
||||
|
||||
function showDetail(id) {
|
||||
const listPage = document.getElementById('listPage');
|
||||
const detailPage = document.getElementById('detailPage');
|
||||
|
||||
const data = uldData[id];
|
||||
if (data) {
|
||||
document.getElementById('det-uldTitle').innerText = '基本信息';
|
||||
document.getElementById('det-billNo').innerText = data.billNo;
|
||||
document.getElementById('det-agent').innerText = data.agent;
|
||||
document.getElementById('det-code').innerText = data.code;
|
||||
document.getElementById('det-carrier').innerText = data.carrier;
|
||||
document.getElementById('det-route').innerText = data.route;
|
||||
document.getElementById('det-dest').innerText = data.dest;
|
||||
document.getElementById('det-billType').innerText = data.billType;
|
||||
document.getElementById('det-unNo').innerText = data.unNo;
|
||||
|
||||
// 锁定状态渲染
|
||||
const lockStatusEl = document.getElementById('det-lockStatus');
|
||||
if (data.lockStatus === '锁定') {
|
||||
lockStatusEl.innerHTML = '<iconify-icon icon="mdi:lock" class="mr-1"></iconify-icon>锁定';
|
||||
lockStatusEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-red-50 text-red-600 inline-flex items-center';
|
||||
} else {
|
||||
lockStatusEl.innerHTML = '—';
|
||||
lockStatusEl.className = 'text-xs font-semibold text-gray-800';
|
||||
}
|
||||
|
||||
document.getElementById('det-packType').innerText = data.packType;
|
||||
document.getElementById('det-type').innerText = data.type;
|
||||
document.getElementById('det-outboundTime').innerText = data.outboundTime || '—';
|
||||
document.getElementById('det-itemNameCn').innerText = data.itemNameCn || '—';
|
||||
document.getElementById('det-itemNameEn').innerText = data.itemNameEn || '—';
|
||||
document.getElementById('det-remark').innerText = data.remark || '—';
|
||||
|
||||
// 件重信息
|
||||
document.getElementById('det-pieces').innerText = data.pieces;
|
||||
document.getElementById('det-weight').innerText = data.weight;
|
||||
document.getElementById('det-calcWeight').innerText = data.calcWeight;
|
||||
document.getElementById('det-inPieces').innerText = data.inPieces;
|
||||
document.getElementById('det-inWeight').innerText = data.inWeight;
|
||||
|
||||
// 海关放行标签
|
||||
const customsReleaseEl = document.getElementById('det-customsRelease');
|
||||
if (data.customsRelease === '11') {
|
||||
customsReleaseEl.innerText = '已放行';
|
||||
customsReleaseEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-green-50 text-green-600 inline-block';
|
||||
} else {
|
||||
customsReleaseEl.innerText = data.customsRelease || '未放行';
|
||||
customsReleaseEl.className = 'text-xs font-semibold px-2 py-0.5 rounded-md bg-gray-100 text-gray-600 inline-block';
|
||||
}
|
||||
|
||||
// 渲染仓库批次列表
|
||||
const warehouseContainer = document.getElementById('tab-content-1');
|
||||
warehouseContainer.innerHTML = '';
|
||||
data.warehouse.forEach(item => {
|
||||
warehouseContainer.innerHTML += `
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="flex justify-between items-center pt-3 pb-1.5 px-3 mb-2 border-b border-gray-100">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block"></span>
|
||||
<span class="text-xs font-semibold text-gray-900">批次号: ${item.batch}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-1.5 px-3 pb-2">
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">件数</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.pieces}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">重量</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${parseFloat(item.weight).toFixed(1)} kg</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">航班日期</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.flightDate}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">航班号</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.flightNo}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">始发站</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.route}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">目的站</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${item.dest}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
// 渲染库位信息
|
||||
const locationContainer = document.getElementById('tab-content-2');
|
||||
locationContainer.innerHTML = '';
|
||||
data.locations.forEach(loc => {
|
||||
locationContainer.innerHTML += `
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="flex justify-between items-center pt-3 pb-1.5 px-3 mb-2 border-b border-gray-100">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block"></span>
|
||||
<span class="text-xs font-bold text-gray-900">${loc.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-1.5 px-3 pb-2">
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库人</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${loc.inPerson}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">入库时间</div>
|
||||
<div class="text-xs font-semibold text-gray-800">${loc.inTime}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">出库人</div>
|
||||
<div class="text-xs ${loc.outPerson === '—' ? 'text-gray-500 text-[14px]' : 'text-gray-800'} font-semibold">${loc.outPerson}</div>
|
||||
</div>
|
||||
<div class="bg-gray-50/50 rounded-lg p-2">
|
||||
<div class="text-[10px] text-gray-500 text-[14px] mb-0.5">出库时间</div>
|
||||
<div class="text-xs ${loc.outTime === '—' ? 'text-gray-500 text-[14px]' : 'text-gray-800'} font-semibold">${loc.outTime}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
}
|
||||
switchTab(0);
|
||||
listPage.classList.add('page-hidden-left');
|
||||
detailPage.classList.remove('page-hidden-right');
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
const listPage = document.getElementById('listPage');
|
||||
const detailPage = document.getElementById('detailPage');
|
||||
listPage.classList.remove('page-hidden-left');
|
||||
detailPage.classList.add('page-hidden-right');
|
||||
}
|
||||
|
||||
function switchTab(index) {
|
||||
const tabs = document.querySelectorAll('.tab-item');
|
||||
const contents = document.querySelectorAll('.tab-pane');
|
||||
tabs.forEach((tab, i) => {
|
||||
if (i === index) {
|
||||
tab.classList.add('active');
|
||||
contents[i].classList.remove('hidden');
|
||||
} else {
|
||||
tab.classList.remove('active');
|
||||
contents[i].classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 搜索框聚焦监听
|
||||
document.querySelector('input[placeholder="搜索ULD编号"]').addEventListener('focus', () => {
|
||||
console.log('搜索框已激活');
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
134
documents/5.5寸手持端设计文件/11_进港移库/photo.html
Normal file
134
documents/5.5寸手持端设计文件/11_进港移库/photo.html
Normal file
@@ -0,0 +1,134 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>拍照上传</title>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'PingFang SC';
|
||||
src: local('PingFang SC');
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
background-color: #e2e8f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
.phone-container {
|
||||
width: 375px;
|
||||
height: 812px;
|
||||
background-color: #f5f6fa;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
border: 8px solid #1a1a1a;
|
||||
border-radius: 40px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="phone-container">
|
||||
<!-- 状态栏 -->
|
||||
<div class="h-10 w-full flex justify-between items-center px-8 flex-none bg-[#2563EB] text-white">
|
||||
<span class="text-xs font-semibold">09:41</span>
|
||||
<div class="flex gap-1 items-center">
|
||||
<iconify-icon icon="mdi:signal" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="bg-[#2563EB] text-white h-12 flex items-center justify-between px-4 flex-none">
|
||||
<div class="flex items-center" onclick="closeWin()">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-bold -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium">拍照上传</h1>
|
||||
<div class="w-16"></div>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="flex-1 overflow-y-auto p-4 bg-[#f5f6fa]">
|
||||
<div class="bg-white rounded-xl p-5 shadow">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-lg font-bold">运单照片上传</h2>
|
||||
</div>
|
||||
|
||||
<div id="previewWrap" class="grid grid-cols-3 gap-2 mb-4 min-h-[100px]"></div>
|
||||
|
||||
<input type="file" id="fileInput" accept="image/*" capture="camera" multiple class="hidden">
|
||||
|
||||
<div class="flex gap-3">
|
||||
<button class="flex-1 bg-blue-600 text-white py-2 rounded" onclick="fileInput.click()">
|
||||
拍照/上传
|
||||
</button>
|
||||
<button class="flex-1 bg-green-600 text-white py-2 rounded" onclick="submitPhotos()">
|
||||
确认提交
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const awb = sessionStorage.getItem('currentAwb');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const previewWrap = document.getElementById('previewWrap');
|
||||
let fileList = [];
|
||||
|
||||
// 选择图片
|
||||
fileInput.onchange = function () {
|
||||
for (let file of this.files) {
|
||||
const url = URL.createObjectURL(file);
|
||||
fileList.push({ file, url });
|
||||
renderPreview();
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染预览
|
||||
function renderPreview() {
|
||||
previewWrap.innerHTML = '';
|
||||
fileList.forEach((item, idx) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'relative border rounded h-20 overflow-hidden';
|
||||
div.innerHTML = `
|
||||
<img src="${item.url}" class="w-full h-full object-cover">
|
||||
<button onclick="delImg(${idx})"
|
||||
class="absolute top-1 right-1 w-5 h-5 bg-red-500 text-white rounded-full text-xs flex items-center justify-center">
|
||||
×
|
||||
</button>
|
||||
`;
|
||||
previewWrap.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
// 删除
|
||||
function delImg(index) {
|
||||
fileList.splice(index, 1);
|
||||
renderPreview();
|
||||
}
|
||||
|
||||
// 提交
|
||||
function submitPhotos() {
|
||||
const urls = fileList.map(item => item.url);
|
||||
sessionStorage.setItem('uploadedPhotos', JSON.stringify(urls));
|
||||
closeWin();
|
||||
}
|
||||
|
||||
function closeWin() {
|
||||
window.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
831
documents/5.5寸手持端设计文件/11_进港移库/进港移库.html
Normal file
831
documents/5.5寸手持端设计文件/11_进港移库/进港移库.html
Normal file
@@ -0,0 +1,831 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>进港移库 - 移动端原型</title>
|
||||
<!-- Tailwind CSS -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/tailwindcss.js"></script>
|
||||
<!-- Iconify -->
|
||||
<script src="https://modao.cc/agent-py/static/source/js/iconify-icon.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'PingFang SC';
|
||||
src: local('PingFang SC');
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #e2e8f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
.phone-container {
|
||||
width: 375px;
|
||||
height: 812px;
|
||||
background-color: #f5f6fa;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
border: 8px solid #1a1a1a;
|
||||
border-radius: 40px;
|
||||
}
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.page {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
.page.active {
|
||||
display: flex;
|
||||
}
|
||||
#filter-panel {
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
#filter-panel.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.overlay {
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
.overlay.show {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.btn-active:active {
|
||||
opacity: 0.7;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
.custom-checkbox {
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #cbd5e1;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.custom-checkbox:checked {
|
||||
background-color: #2563EB;
|
||||
border-color: #2563EB;
|
||||
}
|
||||
.custom-checkbox:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.custom-checkbox-round {
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #cbd5e1;
|
||||
border-radius: 50%;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.custom-checkbox-round:checked {
|
||||
background-color: #2563EB;
|
||||
border-color: #2563EB;
|
||||
}
|
||||
.custom-checkbox-round:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
/* 长按拍照浮层核心样式 */
|
||||
.card-action-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border-radius: 20px;
|
||||
z-index: 10;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
.card-action-overlay.show {
|
||||
display: flex;
|
||||
pointer-events: auto;
|
||||
}
|
||||
/* 卡片必须相对定位,浮层才能盖住自身 */
|
||||
.pending-card, #list-shipped > div {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="phone-container">
|
||||
<!-- 状态栏 -->
|
||||
<div class="h-10 w-full flex justify-between items-center px-8 flex-none bg-[#2563EB] text-white">
|
||||
<span class="text-xs font-semibold">09:41</span>
|
||||
<div class="flex gap-1 items-center">
|
||||
<iconify-icon icon="mdi:signal" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:wifi" width="14"></iconify-icon>
|
||||
<iconify-icon icon="mdi:battery" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 列表首页 -->
|
||||
<div class="page active" id="page-home">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="bg-[#2563EB] text-white h-12 flex items-center justify-between px-4 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="alert('返回首页')">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-bold -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium">进港移库</h1>
|
||||
<div class="w-16"></div>
|
||||
</div>
|
||||
|
||||
<!-- 滚动内容区域 -->
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar pb-24">
|
||||
<!-- 搜索栏 -->
|
||||
<section class="px-4 pt-4 pb-3 bg-white">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center bg-white border border-gray-200 shadow-sm rounded-[12px] h-10 px-3 gap-2 flex-1">
|
||||
<iconify-icon class="text-gray-400 text-lg flex-none" icon="mdi:magnify"></iconify-icon>
|
||||
<input class="flex-1 bg-transparent text-[14px] text-gray-800 placeholder-gray-400 outline-none border-none rounded-lg px-2" placeholder="搜索运单号" type="text"/>
|
||||
<button class="flex-none text-blue-500">
|
||||
<iconify-icon class="text-lg" icon="mdi:qrcode-scan"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button class="bg-white p-2.5 rounded-full shadow-sm border border-gray-200 text-[#2563EB] flex items-center justify-center flex-none" onclick="toggleFilterDrawer(true)">
|
||||
<iconify-icon class="text-xl" icon="mdi:filter-variant"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Tab切换 -->
|
||||
<div class="flex bg-white border-b sticky top-0 z-20">
|
||||
<button class="flex-1 py-3 text-sm font-bold text-[#1F2937] border-b-2 border-[#1F2937] transition-all" id="tab-pending" onclick="switchTab('pending')">
|
||||
待移库 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5">3</span>
|
||||
</button>
|
||||
<button class="flex-1 py-3 text-sm font-bold text-gray-400 border-b-2 border-transparent transition-all" id="tab-shipped" onclick="switchTab('shipped')">
|
||||
已移库 <span class="bg-[#DBEAF6] text-[#2563EB] text-[11px] font-medium px-1.5 py-0.5 rounded-md ml-1.5">3</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 待移库列表 -->
|
||||
<div class="p-3 pt-4 space-y-3" id="list-pending">
|
||||
<!-- 卡片1 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden"
|
||||
onmousedown="startLongPress(this, event)"
|
||||
onmouseup="endLongPress()"
|
||||
onmouseleave="endLongPress()"
|
||||
ontouchstart="startLongPress(this, event)"
|
||||
ontouchmove="cancelLongPress()"
|
||||
ontouchend="endLongPress()">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">78133363573</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">待移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">NOR</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">100件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">200kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-NKG-LAX</span>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363573', status: '待移库', agent: 'NOR', pieces: '100', weight: '200', goodsName: '电子元器件', carrier: 'MU', route: 'HFE-NKG-LAX', location: 'A区-03货位', code: 'NOR', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 拍照浮层 -->
|
||||
<div class="card-action-overlay" onclick="closeSelf(this)">
|
||||
<button class="bg-purple-500 text-white w-14 h-14 rounded-full shadow-lg shadow-purple-500/30 flex items-center justify-center text-[10px] font-bold leading-tight" onclick="event.stopPropagation();goToPhoto('78133363573')">拍照</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片2 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden"
|
||||
onmousedown="startLongPress(this, event)"
|
||||
onmouseup="endLongPress()"
|
||||
onmouseleave="endLongPress()"
|
||||
ontouchstart="startLongPress(this, event)"
|
||||
ontouchmove="cancelLongPress()"
|
||||
ontouchend="endLongPress()">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">78133363574</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">待移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">PER</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">85件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">150kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-PEK-LAX</span>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363574', status: '待移库', agent: 'PER', pieces: '85', weight: '150', goodsName: '服装鞋帽', carrier: 'CA', route: 'HFE-PEK-LAX', location: 'B区-05货位', code: 'NOR', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action-overlay" onclick="closeSelf(this)">
|
||||
<button class="bg-purple-500 text-white w-14 h-14 rounded-full shadow-lg shadow-purple-500/30 flex items-center justify-center text-[10px] font-bold leading-tight" onclick="event.stopPropagation();goToPhoto('78133363574')">拍照</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 卡片3 -->
|
||||
<div class="pending-card bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden"
|
||||
onmousedown="startLongPress(this, event)"
|
||||
onmouseup="endLongPress()"
|
||||
onmouseleave="endLongPress()"
|
||||
ontouchstart="startLongPress(this, event)"
|
||||
ontouchmove="cancelLongPress()"
|
||||
ontouchend="endLongPress()">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="item-checkbox custom-checkbox-round flex-none" onclick="handleCheckboxClick(this, event)" type="checkbox"/>
|
||||
<div class="text-[#2563EB] text-[15px] font-bold">78133363575</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="bg-orange-100 text-orange-600 text-[11px] px-2 h-5 flex items-center rounded-[6px] font-medium">待移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">DHL</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">200件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">450kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:map-marker"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-NKG-LAX</span>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end border-t border-gray-100 pt-3">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363575', status: '待移库', agent: 'DHL', pieces: '200', weight: '450', goodsName: '精密模具', carrier: '3U', route: 'HFE-NKG-LAX', location: 'C区-01货位', code: 'NOR', counterPerson: '--', counterTime: '--', outboundPerson: '', outboundTime: ''})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action-overlay" onclick="closeSelf(this)">
|
||||
<button class="bg-purple-500 text-white w-14 h-14 rounded-full shadow-lg shadow-purple-500/30 flex items-center justify-center text-[10px] font-bold leading-tight" onclick="event.stopPropagation();goToPhoto('78133363575')">拍照</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已移库列表 -->
|
||||
<div class="p-3 pt-4 space-y-3 hidden" id="list-shipped">
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden"
|
||||
onmousedown="startLongPress(this, event)"
|
||||
onmouseup="endLongPress()"
|
||||
onmouseleave="endLongPress()"
|
||||
ontouchstart="startLongPress(this, event)"
|
||||
ontouchmove="cancelLongPress()"
|
||||
ontouchend="endLongPress()">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">78133363001</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">NOR</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">50件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">80kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-NKG-LAX</span>
|
||||
</div>
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363001', status: '已移库', agent: 'NOR', pieces: '50', weight: '80', pickupNo: 'TH20260701001', goodsName: '精密仪器', carrier: 'MU', route: 'HFE-NKG-LAX', location: 'B区-05货位', code: 'NOR', counterTime: '2026-07-05 10:30', counterPerson: '张三', outboundTime: '2026-07-05 11:45', outboundPerson: '张三'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action-overlay" onclick="closeSelf(this)">
|
||||
<button class="bg-purple-500 text-white w-14 h-14 rounded-full shadow-lg shadow-purple-500/30 flex items-center justify-center text-[10px] font-bold leading-tight" onclick="event.stopPropagation();goToPhoto('78133363001')">拍照</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden"
|
||||
onmousedown="startLongPress(this, event)"
|
||||
onmouseup="endLongPress()"
|
||||
onmouseleave="endLongPress()"
|
||||
ontouchstart="startLongPress(this, event)"
|
||||
ontouchmove="cancelLongPress()"
|
||||
ontouchend="endLongPress()">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">78133363002</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">PER</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">120件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">300kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-PEK-LAX</span>
|
||||
</div>
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363002', status: '已移库', agent: 'PER', pieces: '120', weight: '300', pickupNo: 'TH20260702002', goodsName: '鲜花绿植', carrier: 'CA', route: 'HFE-PEK-LAX', location: 'A区-08货位', code: 'NOR', counterTime: '2026-07-06 09:15', counterPerson: '王五', outboundTime: '2026-07-06 10:30', outboundPerson: '赵六'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action-overlay" onclick="closeSelf(this)">
|
||||
<button class="bg-purple-500 text-white w-14 h-14 rounded-full shadow-lg shadow-purple-500/30 flex items-center justify-center text-[10px] font-bold leading-tight" onclick="event.stopPropagation();goToPhoto('78133363002')">拍照</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-3 overflow-hidden"
|
||||
onmousedown="startLongPress(this, event)"
|
||||
onmouseup="endLongPress()"
|
||||
onmouseleave="endLongPress()"
|
||||
ontouchstart="startLongPress(this, event)"
|
||||
ontouchmove="cancelLongPress()"
|
||||
ontouchend="endLongPress()">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-[#1F2937] text-[15px] font-bold">78133363003</div>
|
||||
<div class="flex items-center gap-1 text-[#1F2937] text-[13px] font-medium">
|
||||
<iconify-icon class="text-[#10B981]" icon="mdi:check-circle" width="16"></iconify-icon>
|
||||
<span>已移库</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-3 flex items-center gap-1">
|
||||
<iconify-icon icon="mdi:airplane" class="text-gray-500 text-[14px]"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">航班信息:20260709/MU2311</span>
|
||||
</div>
|
||||
<div class="bg-[#F3F4F6] rounded-lg p-3 grid grid-cols-3 text-center">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-[#9CA3AF] text-[11px]">特码</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">DHL</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">件数</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">75件</span>
|
||||
</div>
|
||||
<div class="flex flex-col border-l border-gray-200">
|
||||
<span class="text-[#9CA3AF] text-[11px]">重量</span>
|
||||
<span class="text-[#1F2937] text-[14px] font-semibold">160kg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center gap-1.5">
|
||||
<iconify-icon class="text-gray-500 text-[14px]" icon="mdi:account-outline"></iconify-icon>
|
||||
<span class="text-[#6B7280] text-[13px]">出运路径:HFE-NKG-LAX</span>
|
||||
</div>
|
||||
<div class="mt-2 flex justify-end border-t border-gray-50 pt-2">
|
||||
<div class="text-[#2563EB] text-[14px] font-medium flex items-center cursor-pointer btn-active" onclick="showDetail({id: '78133363003', status: '已移库', agent: 'DHL', pieces: '75', weight: '160', pickupNo: 'TH20260703003', goodsName: '生鲜食品', carrier: '3U', route: 'HFE-NKG-LAX', location: 'D区-02货位', code: 'NOR', counterTime: '2026-07-06 14:00', counterPerson: '钱七', outboundTime: '2026-07-06 15:20', outboundPerson: '孙八'})">
|
||||
查看详情 <iconify-icon icon="mdi:chevron-right" width="16"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action-overlay" onclick="closeSelf(this)">
|
||||
<button class="bg-purple-500 text-white w-14 h-14 rounded-full shadow-lg shadow-purple-500/30 flex items-center justify-center text-[10px] font-bold leading-tight" onclick="event.stopPropagation();goToPhoto('78133363003')">拍照</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="absolute bottom-0 w-full bg-white border-t border-gray-200 z-40 flex flex-col pt-4 pb-4" id="bottom-toolbar">
|
||||
<div class="px-4 flex items-center w-full">
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="custom-checkbox-round" id="select-all-cb" onclick="toggleSelePERll(this.checked)" type="checkbox"/>
|
||||
<label class="text-[13px] text-[#1F2937] font-medium cursor-pointer" for="select-all-cb">全选</label>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-[12px] text-[#9CA3AF]">已选 <span id="selected-count">0</span> 项</span>
|
||||
<button class="bg-[#2563EB] text-white text-[15px] font-bold px-5 py-2 rounded-[10px] shadow-lg shadow-blue-500/25 btn-active" onclick="alert('移库操作')">
|
||||
移库
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 运单详情页面 -->
|
||||
<div class="page" id="page-detail">
|
||||
<div class="bg-[#2563EB] text-white h-12 flex items-center justify-between px-4 flex-none">
|
||||
<div class="flex items-center btn-active" onclick="switchPage('page-home')">
|
||||
<iconify-icon icon="mdi:chevron-left" width="28"></iconify-icon>
|
||||
<span class="text-sm font-bold -ml-1">返回</span>
|
||||
</div>
|
||||
<h1 class="text-lg font-medium">运单详情</h1>
|
||||
<div class="w-16"></div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto no-scrollbar pb-8 p-4">
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-hidden border border-gray-100">
|
||||
<div class="bg-[#2563EB] px-4 py-3 flex justify-between items-center text-white">
|
||||
<span class="font-bold text-[17px] leading-tight" id="detail-awb">781-22222620</span>
|
||||
<div class="flex items-center text-[13px] font-normal opacity-90">
|
||||
<span class="w-2 h-2 bg-green-400 rounded-full mr-1.5"></span>
|
||||
<span id="detail-time">2026-05-10 08:30</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="bg-[#EF4444] text-white text-[11px] px-1.5 py-0.5 rounded leading-tight font-medium">国际进港</span>
|
||||
<span class="text-[13px] font-normal text-[#666666] border-l border-gray-300 pl-2">普通货物运输</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-[22px] font-bold text-[#111827] mb-3" id="detail-goods-name">电子元器件</div>
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-3">
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">件数</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-count">55</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">重量</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-weight">622.00 kg</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">特码</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-code">NOR</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">代理</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-agent">NOR</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">出运路径</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-medium" id="detail-route">HFE-PEK-LAX</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-[2px]">
|
||||
<div class="text-[12px] text-[#9CA3AF] font-normal">承运人</div>
|
||||
<div class="text-[15px] text-[#1F2937] font-bold" id="detail-carrier">MU</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 移库信息卡片-->
|
||||
<div class="mt-4 bg-white rounded-xl shadow-sm overflow-hidden border border-gray-200 hidden" id="detail-outbound-card">
|
||||
<div class="bg-[#10B981] px-4 py-2.5">
|
||||
<span class="text-white font-semibold text-[15px]">移库信息</span>
|
||||
</div>
|
||||
<div class="bg-white px-4 py-3 grid grid-cols-2 gap-y-3">
|
||||
<div class="flex flex-col" id="detail-outbound-person-row">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">移库人</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-outbound-person">-</span>
|
||||
</div>
|
||||
<div class="flex flex-col" id="detail-outbound-time-row">
|
||||
<span class="text-[12px] text-[#9CA3AF] font-normal mb-[2px]">移库时间</span>
|
||||
<span class="text-[14px] text-[#1F2937] font-medium" id="detail-outbound-time">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 异常照片-->
|
||||
<div class="mt-4 bg-white rounded-xl shadow-sm overflow-hidden border border-gray-100">
|
||||
<div class="bg-[#F9971E] px-4 py-3 flex items-center">
|
||||
<span class="text-white font-semibold text-[15px]">异常照片</span>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div id="image-preview-list" class="grid grid-cols-3 gap-3">
|
||||
<div class="border border-dashed border-gray-300 rounded-lg h-24 flex items-center justify-center text-gray-400 text-lg">+</div>
|
||||
<div class="border border-dashed border-gray-300 rounded-lg h-24 flex items-center justify-center text-gray-400 text-lg">+</div>
|
||||
<div class="border border-dashed border-gray-300 rounded-lg h-24 flex items-center justify-center text-gray-400 text-lg">+</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部筛选遮罩 -->
|
||||
<div class="overlay absolute inset-0 z-30" id="filter-overlay" onclick="toggleFilterDrawer(false)"></div>
|
||||
|
||||
<!-- 筛选弹窗 -->
|
||||
<div class="absolute bottom-0 w-full bg-white rounded-t-3xl z-50 p-6 shadow-2xl overflow-hidden" id="filter-panel">
|
||||
<div class="relative flex items-center justify-center mb-6">
|
||||
<h2 class="text-center text-[18px] font-bold text-gray-900">筛选条件</h2>
|
||||
<button class="absolute right-0 top-0 text-gray-400" onclick="toggleFilterDrawer(false)">
|
||||
<iconify-icon class="text-2xl" icon="mdi:close"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<!-- 1.航班日期 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班日期</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3 cursor-pointer">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请选择航班日期" readonly="" type="text"/>
|
||||
<iconify-icon class="text-gray-500 text-[14px] flex-none" icon="mdi:calendar" width="18"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 2.航班号 -->
|
||||
<div>
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">航班号</label>
|
||||
<div class="flex items-center bg-white border border-gray-200 rounded-lg h-10 px-3">
|
||||
<input class="flex-1 text-[14px] text-gray-800 placeholder-gray-400 outline-none bg-transparent" placeholder="请输入航班号" type="text" id="filterFlightNo">
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">特码</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择特码</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="block text-[14px] text-gray-700 mb-1.5">运单类型</label>
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3 flex items-center justify-between text-gray-400">
|
||||
<span class="text-sm">请选择特码</span>
|
||||
<iconify-icon icon="mdi:chevron-down"></iconify-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 pt-2">
|
||||
<button class="flex-1 bg-white border border-gray-300 text-gray-600 py-3.5 rounded-xl font-bold btn-active" onclick="alert('已重置')">
|
||||
重置
|
||||
</button>
|
||||
<button class="flex-1 bg-[#2563EB] text-white py-3.5 rounded-xl font-bold shadow-lg shadow-blue-500/20 btn-active" onclick="toggleFilterDrawer(false)">
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 页面切换
|
||||
function switchPage(pageId) {
|
||||
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
||||
document.getElementById(pageId).classList.add('active');
|
||||
if(pageId === 'page-detail'){
|
||||
document.querySelector('#page-detail .no-scrollbar').scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Tab切换
|
||||
function switchTab(type) {
|
||||
const tabPending = document.getElementById('tab-pending');
|
||||
const tabShipped = document.getElementById('tab-shipped');
|
||||
const listPending = document.getElementById('list-pending');
|
||||
const listShipped = document.getElementById('list-shipped');
|
||||
const bottomToolbar = document.getElementById('bottom-toolbar');
|
||||
|
||||
if (type === 'pending') {
|
||||
tabPending.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabPending.classList.remove('text-gray-400', 'border-transparent');
|
||||
tabShipped.classList.add('text-gray-400', 'border-transparent');
|
||||
tabShipped.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
|
||||
listPending.classList.remove('hidden');
|
||||
listShipped.classList.add('hidden');
|
||||
bottomToolbar.style.display = 'flex';
|
||||
} else {
|
||||
tabShipped.classList.add('text-[#1F2937]', 'border-[#1F2937]');
|
||||
tabShipped.classList.remove('text-gray-400', 'border-transparent');
|
||||
tabPending.classList.add('text-gray-400', 'border-transparent');
|
||||
tabPending.classList.remove('text-[#1F2937]', 'border-[#1F2937]');
|
||||
|
||||
listShipped.classList.remove('hidden');
|
||||
listPending.classList.add('hidden');
|
||||
bottomToolbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 全选
|
||||
function toggleSelePERll(checked) {
|
||||
const checkboxes = document.querySelectorAll('.item-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = checked;
|
||||
});
|
||||
updateSelectionCount();
|
||||
}
|
||||
|
||||
// 单选
|
||||
function handleCheckboxClick(checkbox, event) {
|
||||
event.stopPropagation();
|
||||
updateSelectionCount();
|
||||
const allBox = document.getElementById('select-all-cb');
|
||||
const all = Array.from(document.querySelectorAll('.item-checkbox')).every(i => i.checked);
|
||||
allBox.checked = all;
|
||||
}
|
||||
|
||||
// 更新选中数量
|
||||
function updateSelectionCount() {
|
||||
const cnt = document.querySelectorAll('.item-checkbox:checked').length;
|
||||
document.getElementById('selected-count').textContent = cnt;
|
||||
}
|
||||
|
||||
// 图片存储全局对象
|
||||
let photoStore = {};
|
||||
|
||||
// 查看详情
|
||||
function showDetail(data) {
|
||||
if (!data) return;
|
||||
const setText = (id, val) => {
|
||||
const el = document.getElementById(id);
|
||||
if(el) el.textContent = val || '-';
|
||||
};
|
||||
setText('detail-awb', data.id);
|
||||
setText('detail-goods-name', data.goodsName);
|
||||
setText('detail-count', data.pieces);
|
||||
setText('detail-weight', data.weight + (String(data.weight).includes('kg') ? '' : ' kg'));
|
||||
setText('detail-code', data.code);
|
||||
setText('detail-agent', data.agent);
|
||||
setText('detail-carrier', data.carrier);
|
||||
setText('detail-route', data.route || 'HFE-PEK-LAX');
|
||||
|
||||
// 渲染图片:最多3格,有图显示图片,空位虚线+占位
|
||||
const list = document.getElementById('image-preview-list');
|
||||
list.innerHTML = '';
|
||||
const imgs = photoStore[data.id] || [];
|
||||
const maxSlot = 3;
|
||||
for(let i = 0; i < maxSlot; i++){
|
||||
if(imgs[i]){
|
||||
// 已有图片
|
||||
const imgWrap = document.createElement('div');
|
||||
imgWrap.className = 'border border-gray-200 rounded-lg h-24 overflow-hidden';
|
||||
imgWrap.innerHTML = `<img src="${imgs[i]}" class="w-full h-full object-cover">`;
|
||||
list.appendChild(imgWrap);
|
||||
}else{
|
||||
// 空虚线占位框
|
||||
const emptyBox = document.createElement('div');
|
||||
emptyBox.className = 'border border-dashed border-gray-300 rounded-lg h-24 flex items-center justify-center text-gray-400 text-lg';
|
||||
emptyBox.textContent = '+';
|
||||
list.appendChild(emptyBox);
|
||||
}
|
||||
}
|
||||
|
||||
const outboundCard = document.getElementById('detail-outbound-card');
|
||||
if(data.status === '待移库'){
|
||||
outboundCard.classList.add('hidden');
|
||||
}else{
|
||||
outboundCard.classList.remove('hidden');
|
||||
setText('detail-outbound-person', data.outboundPerson);
|
||||
setText('detail-outbound-time', data.outboundTime);
|
||||
}
|
||||
switchPage('page-detail');
|
||||
}
|
||||
|
||||
// 筛选抽屉
|
||||
function toggleFilterDrawer(show) {
|
||||
const panel = document.getElementById('filter-panel');
|
||||
const overlay = document.getElementById('filter-overlay');
|
||||
if(show){
|
||||
panel.classList.add('show');
|
||||
overlay.classList.add('show');
|
||||
}else{
|
||||
panel.classList.remove('show');
|
||||
overlay.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 长按拍照完整逻辑 =====================
|
||||
let longPressTimer = null;
|
||||
const LONG_PRESS_DELAY = 600;
|
||||
let isShowOverlay = false;
|
||||
|
||||
// 关闭单个遮罩
|
||||
function closeSelf(dom) {
|
||||
dom.classList.remove('show');
|
||||
isShowOverlay = false;
|
||||
}
|
||||
// 关闭全部遮罩
|
||||
function closeAllOverlay() {
|
||||
document.querySelectorAll('.card-action-overlay').forEach(item => {
|
||||
item.classList.remove('show');
|
||||
})
|
||||
isShowOverlay = false;
|
||||
}
|
||||
// 开始长按
|
||||
function startLongPress(cardDom, e) {
|
||||
if (e) e.preventDefault();
|
||||
if (isShowOverlay) return;
|
||||
closeAllOverlay();
|
||||
clearTimeout(longPressTimer);
|
||||
longPressTimer = setTimeout(() => {
|
||||
cardDom.querySelector('.card-action-overlay').classList.add('show');
|
||||
isShowOverlay = true;
|
||||
}, LONG_PRESS_DELAY)
|
||||
}
|
||||
// 滑动取消长按
|
||||
function cancelLongPress() {
|
||||
if (!isShowOverlay) clearTimeout(longPressTimer);
|
||||
}
|
||||
// 松开鼠标/手指
|
||||
function endLongPress() {
|
||||
clearTimeout(longPressTimer);
|
||||
}
|
||||
// 跳转拍照页,传递运单号
|
||||
function goToPhoto(awb) {
|
||||
sessionStorage.setItem('currentAwb', awb);
|
||||
window.open('photo.html', '_blank');
|
||||
}
|
||||
// 页面加载读取上传图片
|
||||
window.onload = function() {
|
||||
const uploadData = sessionStorage.getItem('uploadedPhotos');
|
||||
const awb = sessionStorage.getItem('currentAwb');
|
||||
if(uploadData && awb) {
|
||||
photoStore[awb] = JSON.parse(uploadData);
|
||||
sessionStorage.removeItem('uploadedPhotos');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
312
documents/5.5寸手持端适配技术方案.md
Normal file
312
documents/5.5寸手持端适配技术方案.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# 5.5 寸手持端适配技术方案
|
||||
|
||||
> 适用范围:AirLogistics(航空物流信息管理系统)在现有 10 寸平板横屏基础上,增加 5.5 寸手机竖屏形态。
|
||||
> 状态:核心技术已打通并双端实机验证,参考页面「国际出港移库」已完成。
|
||||
> 最后更新:2026-07-28
|
||||
|
||||
---
|
||||
|
||||
## 一、背景与结论
|
||||
|
||||
设计稿位于 `documents/5.5寸手持端设计文件/`,共 11 个业务入口、约 66 个界面(37 主页面 + 17 弹层/筛选面板 + 12 详情页 Tab 子页)。手机版是全新 UI,但**功能与交互保持不变**。
|
||||
|
||||
前置调研结论:**本项目的逻辑与 UI 分离度很好,适配成本可集中在 XML 布局重写。**
|
||||
|
||||
| 层 | 现状 | 手机版可复用性 |
|
||||
|---|---|---|
|
||||
| ViewModel(业务逻辑/网络/校验/统计) | 继承 `BaseViewModel`/`BasePageViewModel`,只持有 `MutableLiveData`,不引用 View | 完全复用 |
|
||||
| Activity | 极薄。如 `IntExpStorageUseActivity` 195 行仅引用 `binding.srl`/`binding.rv`/`binding.viewModel` | 完全复用 |
|
||||
| ViewHolder | 通过 id 访问控件(`binding.ivIcon` 等) | 保留同名 id 即复用 |
|
||||
| Adapter | `CommonAdapter` 由 `itemLayoutId` 驱动 | 完全复用 |
|
||||
| 布局 XML | Pad 横屏专用,三列表单、宽表格式 item | 需全部重写 |
|
||||
| 公共 UI 组件 | `PadSearchLayout` / `PadDataLayoutNew` / `title_tool_bar` 均为 Pad 形态 | 需新增手机版 |
|
||||
|
||||
两个必须先解决的工程前提:
|
||||
|
||||
1. `app/src/main/AndroidManifest.xml` 中 **95 个 Activity 全部为 `screenOrientation="userLandscape"`**(强制横屏)
|
||||
—— 现已统一改为 `unspecified`,见下文「屏幕方向」一节
|
||||
2. 项目**没有任何资源限定符目录**,只有 `res/layout/`
|
||||
|
||||
---
|
||||
|
||||
## 二、核心机制:布局按资源限定符自动切换
|
||||
|
||||
```
|
||||
res/layout-sw600dp/activity_xxx.xml ← 平板(最小宽度 >= 600dp,10 寸约 800dp)
|
||||
res/layout/activity_xxx.xml ← 手机(兜底,5.5 寸约 360~411dp)
|
||||
```
|
||||
|
||||
两个变体**同名**,DataBinding 会生成同一个 Binding 基类 + 两个变体实现:
|
||||
|
||||
```
|
||||
ActivityIntExpMoveBinding.java 公共基类(Activity 中引用的就是它)
|
||||
ActivityIntExpMoveBindingImpl.java 手机变体
|
||||
ActivityIntExpMoveBindingSw600dpImpl.java 平板变体
|
||||
```
|
||||
|
||||
因此 **Activity / ViewModel / ViewHolder 完全不需要设备判断**,`layoutId()` 与 `itemLayoutId` 保持原样。
|
||||
|
||||
### 为什么不用「不同文件名 + 代码判断」
|
||||
|
||||
这是本次实施中被否掉的第一版方案,务必不要走回去:
|
||||
|
||||
```kotlin
|
||||
// ❌ 错误做法
|
||||
override fun layoutId() =
|
||||
if (DeviceUtil.isPhone()) R.layout.activity_xxx_phone else R.layout.activity_xxx
|
||||
```
|
||||
|
||||
`activity_xxx_phone.xml` 生成的是 `ActivityXxxPhoneBinding`,而 `binding` 字段声明的类型是 `ActivityXxxBinding`。
|
||||
泛型擦除让**编译期完全不报错**,但一进入该页面就 `ClassCastException` 崩溃。
|
||||
|
||||
### 变体的强制约束
|
||||
|
||||
| 约束 | 说明 | 保障方式 |
|
||||
|---|---|---|
|
||||
| DataBinding 变量必须完全一致 | 两变体 `<data>` 中 `<variable>` 名称与类型相同 | 编译失败 |
|
||||
| 共用控件 id 双方都要有 | Activity/ViewHolder 引用的 id(`srl`/`rv`/`checkIcon`/`iv_icon` 等) | 见下 |
|
||||
| 单侧独有 id 自动 `@Nullable` | 只在一个变体存在的 id,基类中为可空字段 | Kotlin 强制 null 检查 |
|
||||
|
||||
实测生成的基类字段可空性(出港移库):
|
||||
|
||||
```java
|
||||
@NonNull public final TextView btnMove; // 两变体都有
|
||||
@NonNull public final ImageView checkIcon; // 两变体都有
|
||||
@NonNull public final RecyclerView rv; // 两变体都有
|
||||
@NonNull public final SmartRefreshLayout srl; // 两变体都有
|
||||
@Nullable public final EditText etWaybillNo; // 仅手机变体
|
||||
@Nullable public final PadSearchLayout searchWaybillNo; // 仅平板变体
|
||||
```
|
||||
|
||||
即:**约束由编译器静态保障**,不依赖人工检查。
|
||||
|
||||
---
|
||||
|
||||
## 三、已实现的基础设施
|
||||
|
||||
### 1. `DeviceUtil`(新增)
|
||||
|
||||
`module_base/src/main/java/com/lukouguoji/module_base/util/DeviceUtil.kt`
|
||||
|
||||
按 `smallestScreenWidthDp >= 600` 区分平板/手机。关键点:读取 `Resources.getSystem()`(系统资源)而非应用 Resources,以绕开 AndroidAutoSize 对 density 的动态改写,保证判定稳定。
|
||||
|
||||
```kotlin
|
||||
DeviceUtil.isPhone() // 是否手机
|
||||
DeviceUtil.isTablet() // 是否平板
|
||||
DeviceUtil.describe() // "PHONE(sw=411dp, force=AUTO)",日志排查用
|
||||
DeviceUtil.forceMode = DeviceUtil.ForceMode.PHONE // 调试:平板上强制走手机布局
|
||||
```
|
||||
|
||||
`DeviceUtil.layout(padId, phoneId)` 仅用于极少数确实需要代码级判断的场景(如非 DataBinding 布局),**常规页面一律用资源限定符**。
|
||||
|
||||
### 2. 屏幕方向:Manifest 写 `unspecified` + `BaseActivity` 按形态锁定
|
||||
|
||||
方案演进过了两轮,两次失败都有明确的实测证据,记录如下以免重蹈。
|
||||
|
||||
**方案 A(一期,已废弃)**:Manifest 保持 `userLandscape`,靠 `BaseActivity` 在 `onCreate` 里纠正。
|
||||
缺陷:手机上「先横屏、约 1 秒后转竖屏」+ Activity 重建。因为 `screenOrientation` 由系统在
|
||||
**创建 Activity 窗口时**生效,那时业务代码还没跑,代码只能事后纠正。
|
||||
|
||||
**方案 B(二期,已废弃)**:Manifest 改 `@integer/screen_orientation`,
|
||||
`values/`=portrait、`values-sw600dp/`=userLandscape。**看似优雅,实测是错的**:
|
||||
|
||||
> 系统解析 Manifest 属性时用的是**默认配置,不套用设备限定符**,`values-sw600dp/` 永远不会被选中。
|
||||
> 于是所有 Activity 都拿到了 `values/` 的竖屏值,平板端出现两个回归:
|
||||
> 1. 先竖屏、再被 `BaseActivity` 纠正成横屏(把闪屏从手机搬到了平板)
|
||||
> 2. **AutoSize 在竖屏配置下选了平板竖屏基准 720dp**(density = 1280/720 ≈ 1.78,
|
||||
> 正常应为 1280/1152 ≈ 1.11)→ 交互几次后整个 UI 放大约 1.6 倍
|
||||
>
|
||||
> 验证方法(可复现):把 `values/` 与 `values-sw600dp/` 的取值**互换**,并临时让
|
||||
> `lockOrientationByDevice()` 返回 false 以排除运行时纠正,装到平板上看首屏方向 ——
|
||||
> 结果平板取的是 `values/` 的值,证明限定符被忽略。
|
||||
|
||||
**方案 C(现行)**:Manifest 一律写 `unspecified`,系统按设备**当前物理方向**建窗口
|
||||
(平板横屏摆放 → 横屏起;手机 → 竖屏起,两端起始方向本就正确),
|
||||
`BaseActivity.applyDeviceOrientation()` 再按形态锁定,因方向一致故不产生二次纠正与重建。
|
||||
|
||||
```xml
|
||||
<activity android:name="…"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:screenOrientation="unspecified" />
|
||||
```
|
||||
|
||||
全项目 192 处已统一(`module_p` 等 24 处本就是 `portrait` 的 PDA 页面保持不变)。
|
||||
|
||||
```kotlin
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
applyDeviceOrientation() // 必须在 super 之前
|
||||
super.onCreate(savedInstanceState)
|
||||
...
|
||||
}
|
||||
|
||||
protected open fun applyDeviceOrientation() {
|
||||
if (!lockOrientationByDevice()) return
|
||||
requestedOrientation = if (DeviceUtil.isPhone(this))
|
||||
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
else
|
||||
ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
}
|
||||
|
||||
protected open fun lockOrientationByDevice(): Boolean = true // 子类可覆写跳过
|
||||
```
|
||||
|
||||
**已知边界**:若平板在启动瞬间被竖持且系统自动旋转开启,仍会先竖后横。
|
||||
现场平板为横屏固定安装,实测无此现象。
|
||||
|
||||
**验证**:手机冷启动连拍 6 帧全为竖屏;平板冷启动连拍 6 帧全为横屏(1280×800),
|
||||
登录后多次切换菜单与进出页面,UI 尺寸正常无放大。
|
||||
|
||||
### 3. AutoSize 设计基准(易漏,后果严重)
|
||||
|
||||
`MyApplication.initAutoSizeConfig()` 原先只按方向切换,现增加手机分支:
|
||||
|
||||
| 场景 | 设计基准 |
|
||||
|---|---|
|
||||
| 横屏(平板) | 1152 × 720 dp |
|
||||
| 竖屏 + 手机 | **390 × 844 dp** ← 本次新增 |
|
||||
| 竖屏 + 平板 | 720 × 1280 dp |
|
||||
| PictureSelector | 360 × 480 dp |
|
||||
|
||||
**若手机沿用平板竖屏的 720dp 基准,手机上所有 dp 会被压缩约一半,布局全部错位。**
|
||||
|
||||
### 4. 手机端公共资源
|
||||
|
||||
| 资源 | 内容 |
|
||||
|---|---|
|
||||
| `colors.xml` | `phone_primary`(#2563EB)、`phone_primary_light`、`phone_bg`、`phone_text_title/body/hint`、`phone_divider`、`phone_tag_*` |
|
||||
| `bg_phone_card.xml` | 列表卡片:白底 + 12dp 圆角 + 0.5dp 边框 |
|
||||
| `bg_phone_search.xml` | 搜索框:白底 + 12dp 圆角 + 1dp 边框 |
|
||||
| `bg_phone_btn_primary.xml` | 主按钮:主色实底 + 10dp 圆角 + 按下态 |
|
||||
| `bg_phone_tag_gray/green.xml` | 状态标签底色 |
|
||||
| `layout/title_tool_bar.xml` | 手机版标题栏(平板版已移至 `layout-sw600dp/`),保留 `toolbar`/`tool_back`/`title_name` id,`setBackArrow()` 零改动复用 |
|
||||
|
||||
---
|
||||
|
||||
## 四、手机版 UI 规范
|
||||
|
||||
| 部位 | 规范 |
|
||||
|---|---|
|
||||
| 画布基准 | 390 × 844 dp |
|
||||
| 顶栏 | 高 48dp,`phone_primary`,左「‹ 返回」,标题居中 18sp 加粗 |
|
||||
| 页面背景 | `phone_bg` (#F3F4F6) |
|
||||
| 列表卡片 | `bg_phone_card`,`marginHorizontal=16dp`,`marginTop=12dp`,`padding=14dp` |
|
||||
| 卡片头行 | 圆形勾选框 20dp(`radiobtn_checked_style`/`radiobtn_unchecked_style`)+ 主字段 15sp 加粗 + 状态标签 |
|
||||
| 卡片字段 | 两列网格,13sp,`phone_text_body`,行距 8dp |
|
||||
| 状态标签 | 绿底=已完成 / 灰底=未完成,11sp,高 20dp |
|
||||
| 搜索行 | 搜索框高 40dp(放大镜 + 输入 + 扫码)+ 右侧筛选按钮 40dp |
|
||||
| 状态 Tab | 高 42dp,选中文字 `phone_primary` + 底部 28×3dp 指示条 |
|
||||
| 底部操作条 | 白底 + `elevation=8dp`,全选 + 统计(13sp/11sp 两行)+ 主按钮 |
|
||||
| 筛选弹层 | 遮罩 `#80000000` + 底部白面板,重置 / 确定各占一半 |
|
||||
|
||||
---
|
||||
|
||||
## 五、页面改造步骤
|
||||
|
||||
1. **移动平板布局**
|
||||
```bash
|
||||
mkdir -p module_xxx/src/main/res/layout-sw600dp
|
||||
git mv module_xxx/src/main/res/layout/activity_xxx.xml module_xxx/src/main/res/layout-sw600dp/activity_xxx.xml
|
||||
git mv module_xxx/src/main/res/layout/item_xxx.xml module_xxx/src/main/res/layout-sw600dp/item_xxx.xml
|
||||
```
|
||||
2. **新建手机布局**:在 `res/layout/` 下创建**同名**文件,保持相同 `<variable>` 与共用控件 id
|
||||
3. **Kotlin 侧不动**:`layoutId()`、`itemLayoutId`、ViewHolder 全部保持原样
|
||||
4. **纯 UI 交互写在 Activity**,不污染 ViewModel;状态过滤优先复用 ViewModel 既有字段
|
||||
5. 手机布局顶部 `<include layout="@layout/title_tool_bar" />` 不变(变体自动切换)
|
||||
|
||||
### 示例:状态 Tab 零 ViewModel 改动
|
||||
|
||||
出港移库的 `moveState` 字段(`""` 全部 / `"0"` 未移库 / `"1"` 已移库)与查询参数本就存在,Tab 直接复用:
|
||||
|
||||
```kotlin
|
||||
// IntExpMoveActivity 中新增纯 UI 方法
|
||||
fun switchTab(state: String) {
|
||||
if (viewModel.moveState.value == state) return
|
||||
viewModel.moveState.value = state
|
||||
viewModel.searchClick() // 复用原有查询链路
|
||||
}
|
||||
```
|
||||
|
||||
```xml
|
||||
<!-- 判等把字面量放前面,避免 LiveData 为 null 时崩溃 -->
|
||||
android:textColor="@{`0`.equals(viewModel.moveState) ? @color/phone_primary : @color/phone_text_body}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、参考页面实施结果:国际出港移库
|
||||
|
||||
| 文件 | 改动 |
|
||||
|---|---|
|
||||
| `IntExpMoveViewModel.kt` | **0 行业务改动**(仅补注释) |
|
||||
| `IntExpMoveViewHolder.kt` | **完全未改** |
|
||||
| `IntExpMoveActivity.kt` | 仅新增 4 个纯 UI 方法:`switchTab()` / `toggleFilterPanel()` / `resetFilter()` / `confirmFilter()`,以及 `filterPanelVisible` 状态 |
|
||||
| `layout-sw600dp/activity_int_exp_move.xml` | 原平板布局,内容未变,仅移动目录 |
|
||||
| `layout-sw600dp/item_int_exp_move.xml` | 同上 |
|
||||
| `layout/activity_int_exp_move.xml` | 新增手机版 |
|
||||
| `layout/item_int_exp_move.xml` | 新增手机版 |
|
||||
|
||||
---
|
||||
|
||||
## 七、双端验证
|
||||
|
||||
| | 手机模拟器 | 平板模拟器 |
|
||||
|---|---|---|
|
||||
| AVD | `Medium_Phone_API_36.1` | `Aerologic_Tablet` |
|
||||
| 屏幕 | 1080×2400 @420dpi | 1280×800 @160dpi |
|
||||
| 形态识别 | `PHONE(sw=411dp, force=AUTO)` | `TABLET(sw=800dp, force=AUTO)` |
|
||||
| 方向 | 竖屏(`unspecified` + BaseActivity 锁定,无闪屏) | 横屏 |
|
||||
| 加载布局 | 手机版 | 原平板布局,**行为无变化** |
|
||||
| 交互 | Tab 切换选中态迁移并触发查询;筛选弹层正常弹出/关闭 | — |
|
||||
|
||||
APK 内两套变体均正确打包:
|
||||
|
||||
```
|
||||
res/layout-sw600dp-v13/activity_int_exp_move.xml
|
||||
res/layout-sw600dp-v13/item_int_exp_move.xml
|
||||
res/layout-sw600dp-v13/title_tool_bar.xml
|
||||
res/layout/activity_int_exp_move.xml
|
||||
res/layout/item_int_exp_move.xml
|
||||
res/layout/title_tool_bar.xml
|
||||
```
|
||||
|
||||
### 验证命令
|
||||
|
||||
```bash
|
||||
~/Library/Android/sdk/emulator/emulator -list-avds
|
||||
~/Library/Android/sdk/emulator/emulator -avd Medium_Phone_API_36.1 -no-snapshot-load -no-boot-anim &
|
||||
|
||||
adb -s <设备> install -r -t app/build/outputs/apk/debug/app-debug.apk
|
||||
adb -s <设备> shell wm size && adb -s <设备> shell wm density
|
||||
adb -s <设备> logcat -d -s BaseActivity:D # 查看形态识别结果
|
||||
adb -s <设备> exec-out screencap -p > /tmp/shot.png
|
||||
```
|
||||
|
||||
### 调试便利
|
||||
|
||||
`app/src/debug/AndroidManifest.xml`(**仅 Debug 生效,不进 Release 包**)用 `tools:replace` 将适配调试页临时置为 `exported="true"`,免去每次登录再逐级点入:
|
||||
|
||||
```bash
|
||||
adb shell am start -n com.lukouguoji.aerologic/com.lukouguoji.gjc.page.move.IntExpMoveActivity
|
||||
```
|
||||
|
||||
新增调试页时在该文件追加对应 `<activity>` 即可。
|
||||
|
||||
---
|
||||
|
||||
## 八、待办
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| `PhoneDataLayout` 手机版表单组件 | 对标 `PadDataLayoutNew`(319 行 kt)+ `DataLayoutKtx`(404 行)+ `AutoQueryManager`(179 行),支持 INPUT/SPINNER/DATE + required + AutoQuery |
|
||||
| `PhoneSearchBar` 手机版搜索组件 | 当前手机版搜索框为原生 `EditText`,组件化后恢复运单号 AutoQuery 联想能力 |
|
||||
| 筛选弹层内控件替换 | 当前仍复用 `PadSearchLayout`(功能完整、视觉待统一) |
|
||||
| 首页 / 登录 / 我的 | **设计稿缺失**,是手机端使用闭环的前置条件 |
|
||||
|
||||
---
|
||||
|
||||
## 九、注意事项
|
||||
|
||||
1. 改动 `BaseActivity` / `BaseBindingActivity` / 公共组件后,**必须在平板上回归**,确认现有平板版行为不变
|
||||
2. 未适配手机的页面,布局保留在 `res/layout/` 即可(两端共用),手机上视觉不佳但不会崩溃——支持渐进式适配
|
||||
3. 手机版资源统一使用 `phone_*` 颜色与 `bg_phone_*` drawable,禁止直接写死色值
|
||||
4. 两个 git 仓库(`aerologic-app` 与 `air-cargo`)为同一份源码的独立副本,适配改动需同步
|
||||
BIN
documents/后端接口对接问题清单.xlsx
Normal file
BIN
documents/后端接口对接问题清单.xlsx
Normal file
Binary file not shown.
BIN
documents/航空货运APP-5.5寸手持端UI适配-工时报价明细.xlsx
Normal file
BIN
documents/航空货运APP-5.5寸手持端UI适配-工时报价明细.xlsx
Normal file
Binary file not shown.
BIN
documents/航空货运APP-5.5寸手持端适配报价单(对外).xlsx
Normal file
BIN
documents/航空货运APP-5.5寸手持端适配报价单(对外).xlsx
Normal file
Binary file not shown.
BIN
documents/航空货运APP-5.5寸手持端适配报价单.xlsx
Normal file
BIN
documents/航空货运APP-5.5寸手持端适配报价单.xlsx
Normal file
Binary file not shown.
BIN
documents/长卿科技-航空货运作业管理系统报价.xlsx
Normal file
BIN
documents/长卿科技-航空货运作业管理系统报价.xlsx
Normal file
Binary file not shown.
@@ -1,33 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.lukouguoji.module_base">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/logot"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/logot"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Aerologic">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:screenOrientation="userLandscape"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- 屏幕适配 -->
|
||||
<meta-data
|
||||
android:name="design_width_in_dp"
|
||||
android:value="1152" />
|
||||
<meta-data
|
||||
android:name="design_height_in_dp"
|
||||
android:value="720" />
|
||||
</application>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.lukouguoji.module_base">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/logot"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/logot"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Aerologic">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:screenOrientation="unspecified"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- 屏幕适配 -->
|
||||
<meta-data
|
||||
android:name="design_width_in_dp"
|
||||
android:value="1152" />
|
||||
<meta-data
|
||||
android:name="design_height_in_dp"
|
||||
android:value="720" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -2,6 +2,7 @@ package com.lukouguoji.module_base
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
@@ -26,6 +27,7 @@ import com.yanzhenjie.permission.runtime.Permission
|
||||
import com.yzq.zxinglibrary.android.CaptureActivity
|
||||
import com.yzq.zxinglibrary.bean.ZxingConfig
|
||||
import com.yzq.zxinglibrary.common.Constant
|
||||
import com.lukouguoji.module_base.util.DeviceUtil
|
||||
import dev.utils.app.HandlerUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -49,15 +51,48 @@ open class BaseActivity : AppCompatActivity(), CoroutineScope {
|
||||
private var timerTask: TimerTask? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// 必须在 super.onCreate 之前设置,避免进入页面后再旋转造成闪屏与重建
|
||||
applyDeviceOrientation()
|
||||
super.onCreate(savedInstanceState)
|
||||
// requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||
setContentView(R.layout.activity_base)
|
||||
Log.d("BaseActivity", javaClass.simpleName)
|
||||
Log.d("BaseActivity", "${javaClass.simpleName} ${DeviceUtil.describe()}")
|
||||
ActivityCollector.addActivity(this)
|
||||
ARouter.getInstance().inject(this); /////////必须要写这行,自动注入解析
|
||||
loadingDialog = LoadingDialog(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 按设备形态锁定屏幕方向 —— **兜底手段**
|
||||
*
|
||||
* 方向的主控权在 Manifest:各 Activity 统一写
|
||||
* `android:screenOrientation="@integer/screen_orientation"`,
|
||||
* 由资源限定符取值(`values/` = portrait,`values-sw600dp/` = userLandscape)。
|
||||
* 这样方向在**窗口创建阶段**就已确定,不会出现"先横屏、约 1 秒后转竖屏"的闪屏。
|
||||
*
|
||||
* 本方法只作为安全网:万一某个 Activity 的 Manifest 漏配(仍写死 userLandscape),
|
||||
* 至少还能在手机上纠正为竖屏——但那种情况下闪屏依旧存在,应当去补 Manifest。
|
||||
*
|
||||
* 子类如需特殊方向(如扫码页),覆写 [lockOrientationByDevice] 返回 false 即可跳过。
|
||||
*/
|
||||
protected open fun applyDeviceOrientation() {
|
||||
if (!lockOrientationByDevice()) return
|
||||
try {
|
||||
requestedOrientation = if (DeviceUtil.isPhone(this)) {
|
||||
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
} else {
|
||||
ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 半透明 / 悬浮类 Activity 不允许指定方向,忽略即可
|
||||
Log.w("BaseActivity", "setRequestedOrientation failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否由基类按设备形态自动锁定方向,默认开启
|
||||
*/
|
||||
protected open fun lockOrientationByDevice(): Boolean = true
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
job.cancel()
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cretin.www.cretinautoupdatelibrary.model.UpdateConfig
|
||||
import com.cretin.www.cretinautoupdatelibrary.utils.AppUpdateUtils
|
||||
import com.cretin.www.cretinautoupdatelibrary.utils.SSLUtils.TrustAllHostnameVerifier
|
||||
import com.lukouguoji.module_base.handler.MyCrashHandler
|
||||
import com.lukouguoji.module_base.util.DeviceUtil
|
||||
import com.lukouguoji.module_base.util.OkHttp3Connection
|
||||
import com.scwang.smart.refresh.footer.ClassicsFooter
|
||||
import com.scwang.smart.refresh.header.ClassicsHeader
|
||||
@@ -79,20 +80,26 @@ class MyApplication : Application() {
|
||||
.setScreenHeight(activity.resources.displayMetrics.heightPixels)
|
||||
//根据屏幕方向,设置设计尺寸
|
||||
if (activity.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
//设置横屏设计尺寸
|
||||
//设置横屏设计尺寸(平板)
|
||||
AutoSizeConfig.getInstance()
|
||||
.setDesignWidthInDp(1152)
|
||||
.setDesignHeightInDp(720)
|
||||
.setDesignWidthInDp(DeviceUtil.TABLET_DESIGN_WIDTH_DP_LAND)
|
||||
.setDesignHeightInDp(DeviceUtil.TABLET_DESIGN_HEIGHT_DP_LAND)
|
||||
} else {
|
||||
if(activity.javaClass.simpleName.startsWith("PictureSelectorSupporterActivity")){
|
||||
AutoSizeConfig.getInstance()
|
||||
.setDesignWidthInDp(360)
|
||||
.setDesignHeightInDp(480)
|
||||
}else{
|
||||
//设置竖屏设计尺寸
|
||||
} else if (DeviceUtil.isPhone(activity)) {
|
||||
//手机竖屏:按 5.5 寸手持端设计稿画布 390 × 844 换算
|
||||
//若沿用平板竖屏的 720 × 1280 基准,手机上所有 dp 会被压缩约一半,布局全部错位
|
||||
AutoSizeConfig.getInstance()
|
||||
.setDesignWidthInDp(720)
|
||||
.setDesignHeightInDp(1280)
|
||||
.setDesignWidthInDp(DeviceUtil.PHONE_DESIGN_WIDTH_DP)
|
||||
.setDesignHeightInDp(DeviceUtil.PHONE_DESIGN_HEIGHT_DP)
|
||||
} else {
|
||||
//设置竖屏设计尺寸(平板)
|
||||
AutoSizeConfig.getInstance()
|
||||
.setDesignWidthInDp(DeviceUtil.TABLET_DESIGN_WIDTH_DP_PORT)
|
||||
.setDesignHeightInDp(DeviceUtil.TABLET_DESIGN_HEIGHT_DP_PORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,17 @@ abstract class BaseBindingActivity<T : ViewDataBinding, VM : ViewModel> : BaseAc
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
//dataBinding初始化
|
||||
/*
|
||||
* dataBinding初始化
|
||||
*
|
||||
* 平板 / 手机双形态的布局切换由 Android 资源限定符自动完成,此处无需分支:
|
||||
* res/layout-sw600dp/xxx.xml → 平板(最小宽度 >= 600dp)
|
||||
* res/layout/xxx.xml → 手机(兜底)
|
||||
* 两个变体同名,DataBinding 会生成同一个 Binding 类,因此 Activity 与 ViewModel 无需改动。
|
||||
*
|
||||
* 注意:不要按「不同文件名 + 代码判断」的方式切换布局,
|
||||
* 那样会生成两个不同的 Binding 类,泛型擦除下编译期无法发现,运行时必然 ClassCastException。
|
||||
*/
|
||||
binding = DataBindingUtil.setContentView(this, layoutId())
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
@@ -37,6 +47,9 @@ abstract class BaseBindingActivity<T : ViewDataBinding, VM : ViewModel> : BaseAc
|
||||
|
||||
/**
|
||||
* 布局ID
|
||||
*
|
||||
* 双形态适配时保持同一个 id 即可,具体加载哪份布局由资源限定符决定:
|
||||
* res/layout-sw600dp/ 平板 res/layout/ 手机
|
||||
*/
|
||||
abstract fun layoutId(): Int
|
||||
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
package com.lukouguoji.module_base.bean
|
||||
|
||||
class ActionBean(
|
||||
val title: String,
|
||||
val icon: Int,
|
||||
val key: String = ""
|
||||
) {
|
||||
}
|
||||
package com.lukouguoji.module_base.bean
|
||||
|
||||
/**
|
||||
* 手持端(手机版)首页菜单项
|
||||
*
|
||||
* @param title 菜单显示名称
|
||||
* @param icon 菜单图标
|
||||
* @param key 权限串(取 [com.lukouguoji.module_base.common.Constant.AuthName] 中的常量)。
|
||||
* 菜单按登录返回的 authList 是否包含该串来决定显不显示
|
||||
* @param route ARouter 路由路径。填了就由通用逻辑直接跳转,无需再写 when 分支;
|
||||
* 留空表示由所在 ViewModel 的 onItemClick 自行处理(module_p 内的 PDA 页面走这条)
|
||||
*/
|
||||
class ActionBean(
|
||||
val title: String,
|
||||
val icon: Int,
|
||||
val key: String = "",
|
||||
val route: String = ""
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -53,4 +53,16 @@ data class GjcCheckInRecord(
|
||||
onDataChanged?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
// 托盘自重的字符串表示(用于双向绑定,5.5寸手机版「修改记录」覆层)
|
||||
@get:Bindable
|
||||
var carWeightStr: String
|
||||
get() = carWeight
|
||||
set(value) {
|
||||
if (carWeight != value) {
|
||||
carWeight = value
|
||||
notifyPropertyChanged(BR.carWeightStr)
|
||||
onDataChanged?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.lukouguoji.module_base.bean
|
||||
|
||||
import androidx.databinding.ObservableBoolean
|
||||
import java.io.Serializable
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
@@ -75,6 +76,8 @@ data class GjcMaWb(
|
||||
var carNumber: String? = null, // 车牌号
|
||||
var passageWay: String? = null, // 通道号
|
||||
var passageWayId: String? = null,
|
||||
var palletNumber: String? = null, // 托盘数量(5.5寸手机版开始计重页,后端暂无该入参,见接口问题清单)
|
||||
var carWeight: String? = null, // 托盘自重(5.5寸手机版手输,后端暂无该入参,见接口问题清单)
|
||||
|
||||
// ==================== 状态信息 ====================
|
||||
var checkIn: String? = null, // 收运状态。0:待收运,1:已收运,2:收运中
|
||||
@@ -120,12 +123,59 @@ data class GjcMaWb(
|
||||
val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态
|
||||
@Transient
|
||||
val showMore: ObservableBoolean = ObservableBoolean(false) // 展开状态
|
||||
@Transient
|
||||
val actionMenuVisible: ObservableBoolean = ObservableBoolean(false) // 5.5寸手机版:卡片长按操作浮层显隐
|
||||
|
||||
// 兼容现有API的isSelected属性
|
||||
var isSelected: Boolean
|
||||
get() = checked.get()
|
||||
set(value) = checked.set(value)
|
||||
|
||||
/**
|
||||
* 5.5寸手机版:是否处于「待运抵」Tab(申报状态为空)。
|
||||
* 接口无按申报状态区分的查询参数,Tab 由客户端对已加载数据拆分。
|
||||
*/
|
||||
val isPending: Boolean
|
||||
get() = declareStatus.isNullOrEmpty()
|
||||
|
||||
/**
|
||||
* 5.5寸手机版:申报状态三色标签的着色分类(01=绿/W=靛蓝/其余非空=琥珀)。
|
||||
*/
|
||||
val declareTagColor: String
|
||||
get() = when (declareStatus) {
|
||||
"01" -> "green"
|
||||
"W" -> "indigo"
|
||||
else -> if (declareStatus.isNullOrEmpty()) "" else "amber"
|
||||
}
|
||||
|
||||
/**
|
||||
* 5.5寸手机版-出港查询:卡片状态标签。
|
||||
* 已离港 = 航班关闭时间(fclose)非空;已入库 = 入库时间(opDate)非空;都为空 = 未入库(灰)。
|
||||
* 接口无独立状态出参,此判定为前端推断(与卡片「入库时间/离港时间」两列同源)。
|
||||
*/
|
||||
val isQueryDeparted: Boolean
|
||||
get() = !fclose.isNullOrEmpty()
|
||||
|
||||
val isQueryStored: Boolean
|
||||
get() = !opDate.isNullOrEmpty()
|
||||
|
||||
/** 5.5寸手机版:空值统一占位 "--"(空串与 null 都兜底,布局绑定用) */
|
||||
fun orDash(value: String?): String = if (value.isNullOrEmpty()) "--" else value
|
||||
|
||||
/**
|
||||
* 5.5寸手机版:代理人展示值(名称优先,空则回退代码)。
|
||||
* 不能在 XML 里用 `??` 兜底——转换器会把 JSON null 归一成空串,`??` 只判 null 不判空串。
|
||||
*/
|
||||
val agentDisplay: String?
|
||||
get() = if (agentName.isNullOrEmpty()) agentCode else agentName
|
||||
|
||||
/**
|
||||
* 5.5寸手机版-出港仓库:卡片状态标签(已清仓=绿 / 未清仓=橙,clearNormal 为空视作未清仓)。
|
||||
* 已清仓时卡片「清仓」按钮置灰不可点。
|
||||
*/
|
||||
val isCleared: Boolean
|
||||
get() = clearNormal == "1"
|
||||
|
||||
// ==================== 状态转换扩展属性 ====================
|
||||
/**
|
||||
* 计重状态中文
|
||||
@@ -275,15 +325,36 @@ data class GjcHaWb(
|
||||
var tallyOpId: String? = null, // 理货操作人ID
|
||||
var declareCount: Long? = null, // 申报次数
|
||||
var activeId: Long? = null // 活动ID
|
||||
) {
|
||||
) : Serializable {
|
||||
// ==================== UI扩展字段 ====================
|
||||
@Transient
|
||||
val checked: ObservableBoolean = ObservableBoolean(false) // 选中状态
|
||||
@Transient
|
||||
val actionMenuVisible: ObservableBoolean = ObservableBoolean(false) // 5.5寸手机版:卡片长按操作浮层显隐
|
||||
|
||||
// 兼容现有API的isSelected属性
|
||||
var isSelected: Boolean
|
||||
get() = checked.get()
|
||||
set(value) = checked.set(value)
|
||||
|
||||
/**
|
||||
* 5.5寸手机版:申报状态三色标签的着色分类(01=绿/W=靛蓝/其余非空=琥珀)。
|
||||
* 与 GjcMaWb.declareTagColor 同源逻辑,作用于分单自身的 arrivalStatus。
|
||||
*/
|
||||
val haWbTagColor: String
|
||||
get() = when (arrivalStatus) {
|
||||
"01" -> "green"
|
||||
"W" -> "indigo"
|
||||
else -> if (arrivalStatus.isNullOrEmpty()) "" else "amber"
|
||||
}
|
||||
|
||||
/**
|
||||
* 5.5寸手机版「回执」页:运抵操作时间(格式化)。
|
||||
*/
|
||||
val arrivalOpDateText: String
|
||||
get() = arrivalOpDate?.let {
|
||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(it)
|
||||
} ?: ""
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,4 +56,21 @@ data class GjcMove(
|
||||
var isSelected: Boolean
|
||||
get() = checked.get()
|
||||
set(value) = checked.set(value)
|
||||
|
||||
// ========== 5.5 寸手持端展示辅助字段(仅用于手机版布局,不参与序列化) ==========
|
||||
|
||||
/** 是否已移库(moveState:0-未移交,1-已移交) */
|
||||
val isMoved: Boolean
|
||||
get() = moveState == 1
|
||||
|
||||
/** 品名:优先中文,为空回退英文 */
|
||||
val goodsName: String
|
||||
get() = goodsCn.ifEmpty { goods }
|
||||
|
||||
/**
|
||||
* 详情页字段统一空值占位。
|
||||
* 接口对 businessType / opdate / opId 等字段常返回 null,
|
||||
* 直接绑会显示成 "null" 或空白,这里统一成 "-"。
|
||||
*/
|
||||
fun orDash(value: String?): String = if (value.isNullOrEmpty()) "-" else value
|
||||
}
|
||||
|
||||
@@ -108,4 +108,19 @@ class GjcUldUseBean : Serializable {
|
||||
// 复磅状态文本
|
||||
val wtStatusText: String
|
||||
get() = if (wtDate.isNotEmpty()) "已复磅" else "未复磅"
|
||||
|
||||
// ========== 5.5 寸手持端展示辅助字段(仅用于手机版布局,不参与序列化) ==========
|
||||
|
||||
/** 是否已交接:有交接时间即视为已交接 */
|
||||
val isHandovered: Boolean
|
||||
get() = hoDate.isNotEmpty()
|
||||
|
||||
/**
|
||||
* IMP 代码是否属于危险品类 —— 决定手机版卡片上 IMP 代码标签是红底还是绿底。
|
||||
*
|
||||
* 依据 IATA IMP 特货代码惯例:以 R 开头(RFL/RRY/REX 等)及 ICE 归为危险品。
|
||||
* 若后端另有判定口径,只需改这一处。
|
||||
*/
|
||||
val isDgrDangerous: Boolean
|
||||
get() = dgrCode.startsWith("R", ignoreCase = true) || dgrCode.equals("ICE", ignoreCase = true)
|
||||
}
|
||||
|
||||
@@ -74,4 +74,11 @@ class IntImpPickUpDLVBean : Serializable {
|
||||
var isSelected: Boolean
|
||||
get() = checked.get()
|
||||
set(value) = checked.set(value)
|
||||
|
||||
/**
|
||||
* 5.5寸手机版:是否已出库(出库时间非空)。
|
||||
* 卡片按此切两种形态:待出库(勾选框+橙标签+库位行)/已出库(绿标签+出库人行)。
|
||||
*/
|
||||
val isPickedUp: Boolean
|
||||
get() = dlvTime.isNotEmpty()
|
||||
}
|
||||
|
||||
@@ -54,4 +54,26 @@ data class IntImpQueryBean(
|
||||
// ==================== 其他 ====================
|
||||
var remark: String? = null, // 备注
|
||||
var activeId: Long? = null // 有效值
|
||||
)
|
||||
) {
|
||||
|
||||
/**
|
||||
* 5.5寸手机版-进港查询:卡片状态标签。
|
||||
* 已出库 = 出库时间(dlvTime)非空;已入库 = 入库时间(inDate)非空;都为空 = 未入库(灰)。
|
||||
* 接口无独立状态出参,此判定为前端推断(与平板列表「入库时间/出库时间」两列同源)。
|
||||
*/
|
||||
val isQueryOutbound: Boolean
|
||||
get() = !dlvTime.isNullOrEmpty()
|
||||
|
||||
val isQueryStored: Boolean
|
||||
get() = !inDate.isNullOrEmpty()
|
||||
|
||||
/** 5.5寸手机版:空值统一占位 "--"(空串与 null 都兜底,布局绑定用) */
|
||||
fun orDash(value: String?): String = if (value.isNullOrEmpty()) "--" else value
|
||||
|
||||
/**
|
||||
* 5.5寸手机版:代理人展示值(名称优先,空则回退代码)。
|
||||
* 不能在 XML 里用 `??` 兜底——转换器会把 JSON null 归一成空串,`??` 只判 null 不判空串。
|
||||
*/
|
||||
val agentDisplay: String?
|
||||
get() = if (agentName.isNullOrEmpty()) agentCode else agentName
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.lukouguoji.module_base.interfaces.ICheck
|
||||
class MoveStashBean : ICheck {
|
||||
var activeId: String = "" // null
|
||||
var agentCode: String = "" // HFEXB
|
||||
var agentName: String = "" // 代理人名称(search 出参有定义)
|
||||
var awbType: String = "" // 国际进港(国内中转)
|
||||
var businessType: String = "" // null
|
||||
var by1: String = "" // null
|
||||
@@ -49,4 +50,19 @@ class MoveStashBean : ICheck {
|
||||
override fun getCheckObservable(): ObservableBoolean {
|
||||
return checked
|
||||
}
|
||||
|
||||
// ========== 5.5 寸手持端展示辅助字段(仅前端使用,不参与提交) ==========
|
||||
|
||||
/** 是否已移库:由列表加载端点决定(search=待移库 / searchMoved=已移库),载入时打标 */
|
||||
var isMoved: Boolean = false
|
||||
|
||||
/** 卡片长按「拍照」操作浮层显隐(仅 UI 状态) */
|
||||
val actionMenuVisible = ObservableBoolean(false)
|
||||
|
||||
/** 代理显示:网络层会把 null 归一成空串,须 isNullOrEmpty 双兜底(沿用进港/出港查询口径) */
|
||||
val agentDisplay: String
|
||||
get() = if (agentName.isNotEmpty()) agentName else agentCode
|
||||
|
||||
/** 详情页/卡片字段统一空值占位 */
|
||||
fun orDash(value: String?): String = if (value.isNullOrEmpty()) "-" else value
|
||||
}
|
||||
@@ -19,6 +19,18 @@ data class ULDBean(
|
||||
var status: String = "",
|
||||
// 备注
|
||||
var remark: String = "",
|
||||
// ========== 以下字段接口出参已有(api-doc /eqm/uld/pageQuery),此前 Bean 未解析 ==========
|
||||
// 进港航班号(语义待后端确认,见 documents/后端接口对接问题清单.xlsx)
|
||||
var ifNo: String = "",
|
||||
// 出港航班号
|
||||
var efNo: String = "",
|
||||
// 入库时间
|
||||
var checkInDate: String = "",
|
||||
// 出库时间
|
||||
var checkOutDate: String = "",
|
||||
// ========== 后端暂无对应字段,前端按设计稿预留(提交后端忽略,无副作用) ==========
|
||||
// 来源:空运 / 陆运(后端字段需求已登记清单)
|
||||
var source: String = "",
|
||||
){
|
||||
val checked = ObservableBoolean(false)
|
||||
|
||||
@@ -27,4 +39,23 @@ data class ULDBean(
|
||||
"1" -> "故障"
|
||||
else -> status
|
||||
}
|
||||
|
||||
// ========== 手机版(5.5 寸手持端)三态状态展示 ==========
|
||||
// 设计稿状态为 可用(绿)/占用(橙)/维修(红) 三态;后端当前仅两态(0/1),
|
||||
// 按语义映射 0→可用、1→维修,"2"→占用为后端扩展枚举后的预留(需求已登记清单)。
|
||||
|
||||
/** 手机版状态文案 */
|
||||
val statusPhoneName: String
|
||||
get() = when (status) {
|
||||
"0" -> "可用"
|
||||
"1" -> "维修"
|
||||
"2" -> "占用"
|
||||
else -> statusName()
|
||||
}
|
||||
|
||||
/** 可用(绿色标签) */
|
||||
val isStatusAvailable: Boolean get() = status == "0"
|
||||
|
||||
/** 占用(橙色标签,后端扩展枚举后的预留) */
|
||||
val isStatusOccupied: Boolean get() = status == "2"
|
||||
}
|
||||
@@ -272,6 +272,8 @@ interface Constant {
|
||||
const val IntImpTally = "AppIntImpTally" //理货报告
|
||||
const val IntImpPickUpRecord = "AppIntImpPickUpRecord" //提取记录
|
||||
const val IntImpPickUpDLV = "AppIntImpPickUpDLV" //提取出库
|
||||
// 进港移库:后端暂无该模块与权限串(2026-07-31 api-doc 核对),按 AppIntImp* 命名约定预定义,
|
||||
// 权限串下发前手机首页菜单不会显示该入口(已登记后端接口对接问题清单)
|
||||
const val IntImpAccidentVisa = "AppIntImpAccidentVisa" //事故签证
|
||||
const val GjjManifestListActivity = "AppIntExpManifest" //舱单
|
||||
const val GjjTallyListActivity = "AppIntExpTally" //理货
|
||||
|
||||
@@ -851,6 +851,16 @@ interface Api {
|
||||
@POST("IntExpArrive/deleteDeclare")
|
||||
suspend fun deleteArriveDeclare(@Body data: RequestBody): BaseResultBean<Boolean>
|
||||
|
||||
/**
|
||||
* 国际出港运抵-分单补充收发货人信息(5.5寸手机版新增)
|
||||
*
|
||||
* 后端暂无此接口(api-doc 搜索 IntExpArrive、HaWb 均无匹配),路径为参照 Pad 端国际进港
|
||||
* IntImpAirManifest/complete 命名习惯拟定的建议值,见 documents/后端接口对接问题清单.xlsx。
|
||||
* 请求体为分单数组,仅需 haWbId 与收发货人相关字段。
|
||||
*/
|
||||
@POST("IntExpArrive/haWb/supplement")
|
||||
suspend fun supplementArriveHaWb(@Body data: RequestBody): BaseResultBean<Any>
|
||||
|
||||
/**
|
||||
* 国际出港移库-分页查询
|
||||
* 接口路径: /IntExpMove/pageQuery
|
||||
@@ -1756,6 +1766,12 @@ interface Api {
|
||||
@POST("DomImpMove/search")
|
||||
suspend fun getGnjMoveStashList(@Body data: RequestBody): BaseListBean<MoveStashBean>
|
||||
|
||||
/**
|
||||
* 获取-国内进港移库-已移库列表(移库完成分页搜索,5.5 寸手机端「已移库」Tab)
|
||||
*/
|
||||
@POST("DomImpMove/searchMoved")
|
||||
suspend fun getGnjMoveStashMovedList(@Body data: RequestBody): BaseListBean<MoveStashBean>
|
||||
|
||||
/**
|
||||
* 获取-国内进港移库-列表--运单列表
|
||||
*/
|
||||
|
||||
@@ -104,6 +104,7 @@ object ARouterConstants {
|
||||
|
||||
// 国内进港移库(新版本)
|
||||
const val ACTIVITY_URL_GNJ_YIKU_LIST = "/gnj/GnjYiKuListActivity" //国内进港 移库列表
|
||||
const val ACTIVITY_URL_GNJ_MOVE_STASH_LIST = "/app/GnjMoveStashListActivity" //国内进港 移库(首页菜单实际跳转页,双形态)
|
||||
const val ACTIVITY_URL_GNJ_YIKU_DETAILS = "/gnj/GnjYiKuDetailsActivity" //国内进港 移库详情
|
||||
const val ACTIVITY_URL_GNJ_YIKU_EDIT = "/gnj/GnjYiKuEditActivity" //国内进港 移库编辑
|
||||
const val ACTIVITY_URL_GNJ_YIKU_HANDOVER = "/gnj/GnjYiKuHandoverActivity" //国内进港 移交编辑/详情
|
||||
@@ -144,6 +145,8 @@ object ARouterConstants {
|
||||
const val ACTIVITY_URL_INT_EXP_MOVE = "/gjc/IntExpMoveActivity" //国际出港 出港移库
|
||||
const val ACTIVITY_URL_GJC_ASSEMBLE_ALLOCATE = "/gjc/GjcAssembleAllocateActivity" //国际出港 组装分配
|
||||
const val ACTIVITY_URL_INT_EXP_OUT_HANDOVER = "/gjc/IntExpOutHandoverActivity" //国际出港 出库交接
|
||||
|
||||
const val ACTIVITY_URL_ULD_LIST = "/app/UldListActivity" //综合管理 ULD管理(手机端首页入口用)
|
||||
const val ACTIVITY_URL_INT_EXP_LOAD = "/gjc/IntExpLoadActivity" //国际出港 出港装载
|
||||
const val ACTIVITY_URL_INT_EXP_TALLY = "/gjc/IntExpTallyActivity" //国际出港 出港理货
|
||||
const val ACTIVITY_URL_INT_EXP_ARRIVE = "/gjc/IntExpArriveActivity" //国际出港 出港运抵
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.util.SparseArray
|
||||
import android.view.Gravity
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.lukouguoji.module_base.R
|
||||
|
||||
/**
|
||||
* 手机版底部操作条(5.5 寸手持端公共组件)
|
||||
*
|
||||
* 结构:全选(圆形勾选框 + 文字)+ 已选统计 + 主操作按钮。
|
||||
* 对应设计稿各多选列表页底部的白色操作条。
|
||||
*
|
||||
* XML 用法:
|
||||
* ```xml
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneBottomBar
|
||||
* android:layout_width="match_parent"
|
||||
* android:layout_height="wrap_content"
|
||||
* allChecked="@{viewModel.isAllChecked}"
|
||||
* countText='@{"已选 " + viewModel.selectedCount + " 项"}'
|
||||
* actionText="@{`交接`}"
|
||||
* setOnAllCheckClick="@{(v)-> viewModel.checkAllClick()}"
|
||||
* setOnActionClick="@{(v)-> viewModel.completeHandover()}" />
|
||||
* ```
|
||||
*/
|
||||
class PhoneBottomBar @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
val llCheckAll: LinearLayout
|
||||
val ivCheckAll: ImageView
|
||||
val tvCount: TextView
|
||||
val tvAction: TextView
|
||||
|
||||
/** 全选状态(驱动勾选框图标切换) */
|
||||
var allChecked: Boolean = false
|
||||
set(v) {
|
||||
field = v
|
||||
ivCheckAll.setImageResource(
|
||||
if (v) R.drawable.ic_phone_check_round_checked
|
||||
else R.drawable.ic_phone_check_round_unchecked
|
||||
)
|
||||
}
|
||||
|
||||
/** 已选统计文案,如「已选 3 项」 */
|
||||
var countText: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
tvCount.text = v
|
||||
}
|
||||
|
||||
/** 主按钮文字 */
|
||||
var actionText: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
tvAction.text = v
|
||||
}
|
||||
|
||||
/**
|
||||
* 危险操作样式:浅红底 + 红字 + 垃圾桶图标(如 ULD 管理的批量删除)。
|
||||
* 默认 false 为主色实底按钮,既有页面不受影响。
|
||||
*/
|
||||
var actionDanger: Boolean = false
|
||||
set(v) {
|
||||
field = v
|
||||
if (v) {
|
||||
tvAction.setBackgroundResource(R.drawable.bg_phone_btn_danger)
|
||||
tvAction.setTextColor(ContextCompat.getColor(context, R.color.phone_danger_text))
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.ic_phone_trash)?.apply {
|
||||
val size = (16 * resources.displayMetrics.density).toInt()
|
||||
setBounds(0, 0, size, size)
|
||||
}
|
||||
tvAction.setCompoundDrawables(icon, null, null, null)
|
||||
tvAction.compoundDrawablePadding = (4 * resources.displayMetrics.density).toInt()
|
||||
} else {
|
||||
tvAction.setBackgroundResource(R.drawable.bg_phone_btn_primary)
|
||||
tvAction.setTextColor(ContextCompat.getColor(context, R.color.white))
|
||||
tvAction.setCompoundDrawables(null, null, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
orientation = HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
setBackgroundColor(ContextCompat.getColor(context, R.color.white))
|
||||
elevation = 8 * resources.displayMetrics.density
|
||||
val padH = (16 * resources.displayMetrics.density).toInt()
|
||||
val padV = (14 * resources.displayMetrics.density).toInt()
|
||||
setPadding(padH, padV, padH, padV)
|
||||
|
||||
inflate(context, R.layout.layout_phone_bottom_bar, this)
|
||||
|
||||
llCheckAll = findViewById(R.id.ll_check_all)
|
||||
ivCheckAll = findViewById(R.id.iv_check_all)
|
||||
tvCount = findViewById(R.id.tv_count)
|
||||
tvAction = findViewById(R.id.tv_action)
|
||||
}
|
||||
|
||||
/** 只保存/恢复自身状态,避免内部子控件 id 与其他复合组件实例串档 */
|
||||
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchFreezeSelfOnly(container)
|
||||
}
|
||||
|
||||
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchThawSelfOnly(container)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.util.SparseArray
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.databinding.InverseBindingListener
|
||||
import com.lukouguoji.module_base.R
|
||||
import com.lukouguoji.module_base.adapter.bindAdapter
|
||||
import com.lukouguoji.module_base.adapter.bindOnSelected
|
||||
import com.lukouguoji.module_base.interfaces.IOnSpinnerSelected
|
||||
import com.lukouguoji.module_base.ktx.formatDate
|
||||
import com.lukouguoji.module_base.ktx.getActivity
|
||||
import com.lukouguoji.module_base.util.Common
|
||||
import dev.utils.app.info.KeyValue
|
||||
import java.util.Calendar
|
||||
|
||||
/**
|
||||
* 手机版表单 / 筛选项(5.5 寸手持端公共组件)
|
||||
*
|
||||
* 结构:上方标题(14sp #374151)+ 下方 40dp 高控件框(白底 + 1px 边框 + 8dp 圆角)。
|
||||
* 支持三种形态,由 [type] 决定:
|
||||
*
|
||||
* | type | 控件 | 右侧图标 |
|
||||
* |-----------|-----------|----------|
|
||||
* | `INPUT` | EditText | 无 |
|
||||
* | `DATE` | TextView(点击弹日历)| 日历 |
|
||||
* | `SPINNER` | Spinner | 下箭头 |
|
||||
*
|
||||
* XML 用法:
|
||||
* ```xml
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout
|
||||
* android:layout_width="match_parent"
|
||||
* android:layout_height="wrap_content"
|
||||
* title="@{`航班日期`}"
|
||||
* hint="@{`请选择航班日期`}"
|
||||
* type="@{PhoneDataLayoutType.DATE}"
|
||||
* value="@={viewModel.flightDate}" />
|
||||
* ```
|
||||
*
|
||||
* 手机端专用,与平板端 PadDataLayoutNew 并列存在,互不影响。
|
||||
*/
|
||||
class PhoneDataLayout @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
val tvTitle: TextView
|
||||
val tvMust: TextView
|
||||
val llContent: LinearLayout
|
||||
val et: EditText
|
||||
val tv: TextView
|
||||
val spinner: Spinner
|
||||
val iv: ImageView
|
||||
|
||||
/** 双向绑定回调 */
|
||||
var onChangeListener: InverseBindingListener? = null
|
||||
|
||||
/** 输入完成 / 选择完成回调 */
|
||||
var refreshCallBack: (() -> Unit)? = null
|
||||
|
||||
var type: PhoneDataLayoutType = PhoneDataLayoutType.INPUT
|
||||
set(v) {
|
||||
field = v
|
||||
applyType()
|
||||
applyValue()
|
||||
}
|
||||
|
||||
var title: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
tvTitle.text = v
|
||||
}
|
||||
|
||||
var hint: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
et.hint = v
|
||||
tv.hint = v
|
||||
bindAdapter(spinner, list, v, R.layout.item_spinner_list_14sp)
|
||||
}
|
||||
|
||||
var value: String = ""
|
||||
set(v) {
|
||||
if (field == v) return
|
||||
field = v
|
||||
applyValue()
|
||||
onChangeListener?.onChange()
|
||||
}
|
||||
|
||||
var list: List<KeyValue> = emptyList()
|
||||
set(v) {
|
||||
field = v
|
||||
bindAdapter(spinner, v, hint, R.layout.item_spinner_list_14sp)
|
||||
applyValue()
|
||||
}
|
||||
|
||||
var required: Boolean = false
|
||||
set(v) {
|
||||
field = v
|
||||
tvMust.visibility = if (v) VISIBLE else GONE
|
||||
}
|
||||
|
||||
var enable: Boolean = true
|
||||
set(v) {
|
||||
field = v
|
||||
llContent.isEnabled = v
|
||||
et.isEnabled = v
|
||||
spinner.isEnabled = v
|
||||
}
|
||||
|
||||
init {
|
||||
orientation = VERTICAL
|
||||
inflate(context, R.layout.layout_phone_data_layout, this)
|
||||
|
||||
tvTitle = findViewById(R.id.tv_title)
|
||||
tvMust = findViewById(R.id.tv_must)
|
||||
llContent = findViewById(R.id.ll_content)
|
||||
et = findViewById(R.id.et)
|
||||
tv = findViewById(R.id.tv)
|
||||
spinner = findViewById(R.id.spinner)
|
||||
iv = findViewById(R.id.iv)
|
||||
|
||||
et.doOnTextChanged { text, _, _, _ ->
|
||||
// 仅 INPUT 形态才回写,避免隐藏的 EditText 被系统状态恢复时误清空值
|
||||
if (type == PhoneDataLayoutType.INPUT) {
|
||||
value = text.toString()
|
||||
}
|
||||
}
|
||||
|
||||
bindOnSelected(spinner, object : IOnSpinnerSelected {
|
||||
override fun onSelected(position: Int) {
|
||||
value = list.getOrNull(position)?.value ?: ""
|
||||
refreshCallBack?.invoke()
|
||||
}
|
||||
})
|
||||
|
||||
applyType()
|
||||
}
|
||||
|
||||
private val dateClick: (v: View) -> Unit = {
|
||||
if (enable) {
|
||||
Common.onYearMonthDay(context.getActivity(), value) { year, month, day ->
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.set(year, month - 1, day)
|
||||
value = calendar.time.formatDate()
|
||||
refreshCallBack?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyType() {
|
||||
when (type) {
|
||||
PhoneDataLayoutType.INPUT -> {
|
||||
et.visibility = VISIBLE
|
||||
tv.visibility = GONE
|
||||
spinner.visibility = GONE
|
||||
iv.visibility = GONE
|
||||
llContent.setOnClickListener(null)
|
||||
llContent.isClickable = false
|
||||
}
|
||||
|
||||
PhoneDataLayoutType.DATE -> {
|
||||
et.visibility = GONE
|
||||
tv.visibility = VISIBLE
|
||||
spinner.visibility = GONE
|
||||
iv.visibility = VISIBLE
|
||||
iv.setImageResource(R.drawable.ic_phone_calendar)
|
||||
llContent.setOnClickListener(dateClick)
|
||||
}
|
||||
|
||||
PhoneDataLayoutType.SPINNER -> {
|
||||
et.visibility = GONE
|
||||
tv.visibility = GONE
|
||||
spinner.visibility = VISIBLE
|
||||
iv.visibility = VISIBLE
|
||||
iv.setImageResource(R.drawable.ic_phone_chevron_down)
|
||||
llContent.setOnClickListener(null)
|
||||
llContent.isClickable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyValue() {
|
||||
when (type) {
|
||||
PhoneDataLayoutType.INPUT -> {
|
||||
if (et.text.toString() != value) et.setText(value)
|
||||
}
|
||||
|
||||
PhoneDataLayoutType.DATE -> {
|
||||
tv.text = value
|
||||
}
|
||||
|
||||
PhoneDataLayoutType.SPINNER -> {
|
||||
if (value.isNotEmpty()) {
|
||||
val position = list.indexOfFirst { it.value == value }
|
||||
if (position >= 0) spinner.setSelection(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同一页面往往放置多个本组件实例,而它们的内部子控件(et / tv / spinner)共用同一套 id。
|
||||
* 系统按 id 保存/恢复视图状态会发生跨实例串档(典型表现:日期项被邻近输入框的空文本覆盖)。
|
||||
* 这里只保存/恢复自身状态、不下发给子控件;控件取值本就由 ViewModel + DataBinding 持有,无需视图级恢复。
|
||||
*/
|
||||
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchFreezeSelfOnly(container)
|
||||
}
|
||||
|
||||
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchThawSelfOnly(container)
|
||||
}
|
||||
|
||||
/** 重置为空值(筛选弹层「重置」按钮使用) */
|
||||
fun reset() {
|
||||
value = ""
|
||||
if (type == PhoneDataLayoutType.SPINNER && list.isNotEmpty()) {
|
||||
spinner.setSelection(if (hint.isEmpty()) list.size - 1 else list.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** [PhoneDataLayout] 支持的控件形态 */
|
||||
enum class PhoneDataLayoutType {
|
||||
INPUT, // 文本输入
|
||||
DATE, // 日期选择
|
||||
SPINNER, // 下拉选择
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.util.SparseArray
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.lukouguoji.module_base.R
|
||||
|
||||
/**
|
||||
* 手机版底部筛选弹层(5.5 寸手持端公共组件)
|
||||
*
|
||||
* 半透明遮罩 + 底部白色面板(顶部 20dp 圆角),面板内含标题行、内容插槽、重置/确认按钮行。
|
||||
* 对应设计稿各列表页的「筛选条件」弹层。
|
||||
*
|
||||
* **内容插槽**:直接把筛选项(一般是 [PhoneDataLayout])写在本组件标签内即可,
|
||||
* [onFinishInflate] 会自动把它们移入面板中部,无需关心内部层级:
|
||||
*
|
||||
* ```xml
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel
|
||||
* android:layout_width="match_parent"
|
||||
* android:layout_height="match_parent"
|
||||
* panelVisible="@{activity.filterPanelVisible}"
|
||||
* setOnResetClick="@{(v)-> activity.resetFilter()}"
|
||||
* setOnConfirmClick="@{(v)-> activity.confirmFilter()}"
|
||||
* setOnDismissClick="@{(v)-> activity.toggleFilterPanel()}">
|
||||
*
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout ... />
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneDataLayout ... />
|
||||
*
|
||||
* </com.lukouguoji.module_base.ui.weight.phone.PhoneFilterPanel>
|
||||
* ```
|
||||
*
|
||||
* 相邻筛选项之间的 20dp 间距由本组件统一施加,使用方无需逐个写 marginTop。
|
||||
*/
|
||||
class PhoneFilterPanel @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
/**
|
||||
* 同一页面可能出现多个含内部 id 的手机版复合组件,系统按 id 保存/恢复视图状态会跨实例串档。
|
||||
* 这里只保存/恢复自身状态、不下发给子控件;筛选值由 ViewModel + DataBinding 持有,无需视图级恢复。
|
||||
*/
|
||||
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchFreezeSelfOnly(container)
|
||||
}
|
||||
|
||||
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchThawSelfOnly(container)
|
||||
}
|
||||
|
||||
val llPanel: LinearLayout
|
||||
val llContent: LinearLayout
|
||||
val tvTitle: TextView
|
||||
val ivClose: ImageView
|
||||
val tvReset: TextView
|
||||
val tvConfirm: TextView
|
||||
|
||||
/** 遮罩点击 / 关闭图标点击回调 */
|
||||
private var dismissListener: OnClickListener? = null
|
||||
|
||||
var title: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
if (v.isNotEmpty()) tvTitle.text = v
|
||||
}
|
||||
|
||||
/** 弹层显隐 */
|
||||
var panelVisible: Boolean = false
|
||||
set(v) {
|
||||
field = v
|
||||
visibility = if (v) VISIBLE else GONE
|
||||
}
|
||||
|
||||
init {
|
||||
setBackgroundColor(ContextCompat.getColor(context, R.color.black_tran50))
|
||||
visibility = GONE
|
||||
|
||||
inflate(context, R.layout.layout_phone_filter_panel, this)
|
||||
|
||||
llPanel = findViewById(R.id.ll_panel)
|
||||
llContent = findViewById(R.id.ll_content)
|
||||
tvTitle = findViewById(R.id.tv_title)
|
||||
ivClose = findViewById(R.id.iv_close)
|
||||
tvReset = findViewById(R.id.tv_reset)
|
||||
tvConfirm = findViewById(R.id.tv_confirm)
|
||||
|
||||
// 遮罩点击关闭;面板自身已 clickable,不会穿透
|
||||
setOnClickListener { dismissListener?.onClick(this) }
|
||||
ivClose.setOnClickListener { dismissListener?.onClick(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 XML 中声明在本组件内的子 View 移入面板内容区,并统一施加 20dp 项间距。
|
||||
*/
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
|
||||
// index 0 是内部面板(init 中 inflate 得到),其后才是使用方声明的子 View
|
||||
val declared = mutableListOf<View>()
|
||||
for (i in childCount - 1 downTo 1) {
|
||||
declared.add(0, getChildAt(i))
|
||||
removeViewAt(i)
|
||||
}
|
||||
|
||||
declared.forEachIndexed { index, child ->
|
||||
llContent.addView(child)
|
||||
if (index > 0) {
|
||||
(child.layoutParams as? LinearLayout.LayoutParams)?.let { lp ->
|
||||
lp.topMargin = (20 * resources.displayMetrics.density).toInt()
|
||||
child.layoutParams = lp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setOnDismiss(listener: OnClickListener?) {
|
||||
dismissListener = listener
|
||||
}
|
||||
|
||||
/** 遍历内容区所有 [PhoneDataLayout] 并重置为空值 */
|
||||
fun resetAllFields() {
|
||||
for (i in 0 until llContent.childCount) {
|
||||
(llContent.getChildAt(i) as? PhoneDataLayout)?.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.text.InputType
|
||||
import android.util.AttributeSet
|
||||
import android.util.SparseArray
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.databinding.InverseBindingListener
|
||||
import com.lukouguoji.module_base.R
|
||||
import com.lukouguoji.module_base.ktx.formatDate
|
||||
import com.lukouguoji.module_base.ktx.getActivity
|
||||
import com.lukouguoji.module_base.util.Common
|
||||
import com.lxj.xpopup.XPopup
|
||||
import dev.utils.app.info.KeyValue
|
||||
import java.util.Calendar
|
||||
|
||||
/**
|
||||
* 手机版行式表单项(5.5 寸手持端公共组件)
|
||||
*
|
||||
* 结构:左标签(固定 110dp,+ 必填星)+ 右值区 + 可选箭头 + 底部 1px 分隔线。
|
||||
* 对应设计稿中新增 / 修改 / 详情三态共用的行式表单(如 01_ULD管理)。
|
||||
* 与 [PhoneDataLayout](上下结构,用于筛选弹层)互补,形态见 [PhoneFormRowType]。
|
||||
*
|
||||
* XML 用法(type 按页面状态用三元切换即可覆盖三态页面):
|
||||
* ```xml
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneFormRow
|
||||
* android:layout_width="match_parent"
|
||||
* android:layout_height="wrap_content"
|
||||
* title='@{"所属航司"}'
|
||||
* hint='@{"请选择所属航司"}'
|
||||
* list="@{viewModel.carrierList}"
|
||||
* type="@{viewModel.pageType == DetailsPageType.Details ? PhoneFormRowType.TEXT : PhoneFormRowType.SELECT}"
|
||||
* value="@={viewModel.uldBean.carrier}" />
|
||||
* ```
|
||||
*/
|
||||
class PhoneFormRow @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
val tvTitle: TextView
|
||||
val tvMust: TextView
|
||||
val et: EditText
|
||||
val tv: TextView
|
||||
val ivArrow: ImageView
|
||||
val llRow: LinearLayout
|
||||
|
||||
/** 双向绑定回调 */
|
||||
var onChangeListener: InverseBindingListener? = null
|
||||
|
||||
/** 选择 / 日期确定后的回调 */
|
||||
var refreshCallBack: (() -> Unit)? = null
|
||||
|
||||
var type: PhoneFormRowType = PhoneFormRowType.INPUT
|
||||
set(v) {
|
||||
field = v
|
||||
applyType()
|
||||
applyValue()
|
||||
}
|
||||
|
||||
var title: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
tvTitle.text = v
|
||||
}
|
||||
|
||||
var hint: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
et.hint = v
|
||||
tv.hint = v
|
||||
}
|
||||
|
||||
var value: String = ""
|
||||
set(v) {
|
||||
if (field == v) return
|
||||
field = v
|
||||
applyValue()
|
||||
onChangeListener?.onChange()
|
||||
}
|
||||
|
||||
/** SELECT 形态的选项(key 显示文案 / value 业务值) */
|
||||
var list: List<KeyValue> = emptyList()
|
||||
set(v) {
|
||||
field = v
|
||||
applyValue()
|
||||
}
|
||||
|
||||
var required: Boolean = false
|
||||
set(v) {
|
||||
field = v
|
||||
tvMust.visibility = if (v) VISIBLE else GONE
|
||||
}
|
||||
|
||||
var enable: Boolean = true
|
||||
set(v) {
|
||||
field = v
|
||||
et.isEnabled = v
|
||||
applyType()
|
||||
}
|
||||
|
||||
/** INPUT 形态是否数字键盘(自重等数值项) */
|
||||
var numeric: Boolean = false
|
||||
set(v) {
|
||||
field = v
|
||||
et.inputType = if (v) {
|
||||
InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||
} else {
|
||||
InputType.TYPE_CLASS_TEXT
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
orientation = VERTICAL
|
||||
inflate(context, R.layout.layout_phone_form_row, this)
|
||||
|
||||
llRow = findViewById(R.id.ll_row)
|
||||
tvTitle = findViewById(R.id.tv_title)
|
||||
tvMust = findViewById(R.id.tv_must)
|
||||
et = findViewById(R.id.et)
|
||||
tv = findViewById(R.id.tv)
|
||||
ivArrow = findViewById(R.id.iv_arrow)
|
||||
|
||||
et.doOnTextChanged { text, _, _, _ ->
|
||||
// 仅 INPUT 形态才回写,避免隐藏的 EditText 被系统状态恢复时误清空值
|
||||
if (type == PhoneFormRowType.INPUT) {
|
||||
value = text.toString()
|
||||
}
|
||||
}
|
||||
|
||||
applyType()
|
||||
}
|
||||
|
||||
private val selectClick: (v: View) -> Unit = {
|
||||
if (enable && list.isNotEmpty()) {
|
||||
XPopup.Builder(context)
|
||||
.asBottomList(hint.ifEmpty { title }, list.map { it.key }.toTypedArray()) { position, _ ->
|
||||
value = list.getOrNull(position)?.value ?: ""
|
||||
refreshCallBack?.invoke()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
private val dateClick: (v: View) -> Unit = {
|
||||
if (enable) {
|
||||
Common.onYearMonthDay(context.getActivity(), value) { year, month, day ->
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.set(year, month - 1, day)
|
||||
value = calendar.time.formatDate()
|
||||
refreshCallBack?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyType() {
|
||||
when (type) {
|
||||
PhoneFormRowType.INPUT -> {
|
||||
et.visibility = VISIBLE
|
||||
tv.visibility = GONE
|
||||
ivArrow.visibility = GONE
|
||||
llRow.setOnClickListener(null)
|
||||
llRow.isClickable = false
|
||||
}
|
||||
|
||||
PhoneFormRowType.SELECT -> {
|
||||
et.visibility = GONE
|
||||
tv.visibility = VISIBLE
|
||||
tv.gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
||||
ivArrow.visibility = if (enable) VISIBLE else GONE
|
||||
llRow.setOnClickListener(selectClick)
|
||||
}
|
||||
|
||||
PhoneFormRowType.DATE -> {
|
||||
et.visibility = GONE
|
||||
tv.visibility = VISIBLE
|
||||
tv.gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
||||
ivArrow.visibility = GONE
|
||||
llRow.setOnClickListener(dateClick)
|
||||
}
|
||||
|
||||
PhoneFormRowType.TEXT -> {
|
||||
et.visibility = GONE
|
||||
tv.visibility = VISIBLE
|
||||
tv.gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
||||
tv.hint = ""
|
||||
ivArrow.visibility = GONE
|
||||
llRow.setOnClickListener(null)
|
||||
llRow.isClickable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyValue() {
|
||||
when (type) {
|
||||
PhoneFormRowType.INPUT -> {
|
||||
if (et.text.toString() != value) et.setText(value)
|
||||
}
|
||||
|
||||
PhoneFormRowType.SELECT -> {
|
||||
// 显示选中项的文案;列表里找不到就按原值显示(如后端返回的历史值)
|
||||
tv.text = list.firstOrNull { it.value == value }?.key ?: value
|
||||
}
|
||||
|
||||
PhoneFormRowType.DATE -> {
|
||||
tv.text = value
|
||||
}
|
||||
|
||||
PhoneFormRowType.TEXT -> {
|
||||
// 只读展示:空值统一占位 "--"(与设计稿一致)
|
||||
tv.text = value.ifEmpty { "--" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 只保存/恢复自身状态,避免内部子控件 id 与其他实例串档(公共组件必做,见 components.md) */
|
||||
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchFreezeSelfOnly(container)
|
||||
}
|
||||
|
||||
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchThawSelfOnly(container)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
/**
|
||||
* [PhoneFormRow] 的形态。
|
||||
*
|
||||
* | 形态 | 值区控件 | 交互 |
|
||||
* |----------|--------------------|------|
|
||||
* | `INPUT` | EditText(左对齐) | 直接输入 |
|
||||
* | `SELECT` | TextView + 右箭头 | 点击弹底部选择面板(list 提供选项) |
|
||||
* | `DATE` | TextView | 点击弹日历 |
|
||||
* | `TEXT` | TextView(右对齐) | 只读展示(详情态) |
|
||||
*/
|
||||
enum class PhoneFormRowType {
|
||||
INPUT,
|
||||
SELECT,
|
||||
DATE,
|
||||
TEXT,
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.lukouguoji.module_base.R
|
||||
|
||||
/**
|
||||
* 手机版详情页字段项(5.5 寸手持端公共组件)
|
||||
*
|
||||
* 竖排「小号灰标签(12sp)+ 数值(15sp)」,用于详情页两列信息网格。
|
||||
* 对应设计稿「板箱详情」等详情卡片内的字段。
|
||||
*
|
||||
* XML 用法:
|
||||
* ```xml
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneKvItem
|
||||
* android:layout_width="0dp"
|
||||
* android:layout_height="wrap_content"
|
||||
* android:layout_weight="1"
|
||||
* title="@{`装机重`}"
|
||||
* value="@{bean.netWeight}" />
|
||||
* ```
|
||||
*
|
||||
* 需要突出的字段(如库位)设 `tag="@{true}"`,数值会渲染为橙色高亮标签。
|
||||
*/
|
||||
class PhoneKvItem @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
val tvTitle: TextView
|
||||
val tvValue: TextView
|
||||
|
||||
var title: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
tvTitle.text = v
|
||||
}
|
||||
|
||||
var value: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
// 详情页空值统一显示为 "-",与设计稿一致
|
||||
tvValue.text = v.ifEmpty { "-" }
|
||||
applyStyle()
|
||||
}
|
||||
|
||||
/** 是否以橙色高亮标签样式展示数值 */
|
||||
var tag: Boolean = false
|
||||
set(v) {
|
||||
field = v
|
||||
applyStyle()
|
||||
}
|
||||
|
||||
/** 高亮标签配色:"orange"(默认,如库位)/ "green"(如海关放行正常态) */
|
||||
var tagColor: String = "orange"
|
||||
set(v) {
|
||||
field = v
|
||||
applyStyle()
|
||||
}
|
||||
|
||||
/**
|
||||
* 空值不套高亮标签底 —— 否则占位符 "-"/"--" 会显示成一个孤零零的色块。
|
||||
*/
|
||||
private fun applyStyle() {
|
||||
if (tag && value.isNotEmpty() && value != "-" && value != "--") {
|
||||
if (tagColor == "green") {
|
||||
tvValue.setBackgroundResource(R.drawable.bg_phone_tag_green)
|
||||
tvValue.setTextColor(ContextCompat.getColor(context, R.color.phone_tag_green_text))
|
||||
} else {
|
||||
tvValue.setBackgroundResource(R.drawable.bg_phone_value_orange)
|
||||
tvValue.setTextColor(ContextCompat.getColor(context, R.color.phone_orange_text))
|
||||
}
|
||||
tvValue.setTypeface(tvValue.typeface, android.graphics.Typeface.BOLD)
|
||||
val padH = (4 * resources.displayMetrics.density).toInt()
|
||||
tvValue.setPadding(padH, 0, padH, 0)
|
||||
} else {
|
||||
tvValue.background = null
|
||||
tvValue.setTextColor(ContextCompat.getColor(context, R.color.phone_text_title))
|
||||
tvValue.setPadding(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
orientation = VERTICAL
|
||||
|
||||
tvTitle = TextView(context).apply {
|
||||
textSize = 12f
|
||||
setTextColor(ContextCompat.getColor(context, R.color.phone_text_hint))
|
||||
}
|
||||
addView(
|
||||
tvTitle,
|
||||
LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
||||
)
|
||||
|
||||
tvValue = TextView(context).apply {
|
||||
textSize = 15f
|
||||
setTextColor(ContextCompat.getColor(context, R.color.phone_text_title))
|
||||
}
|
||||
addView(
|
||||
tvValue,
|
||||
LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
|
||||
topMargin = (2 * resources.displayMetrics.density).toInt()
|
||||
gravity = Gravity.START
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.util.SparseArray
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.databinding.InverseBindingListener
|
||||
import com.lukouguoji.module_base.R
|
||||
import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter
|
||||
|
||||
/**
|
||||
* 手机版搜索栏(5.5 寸手持端公共组件)
|
||||
*
|
||||
* 结构:圆角搜索框(放大镜 + 输入框 + 扫码图标)+ 右侧圆形筛选按钮。
|
||||
* 对应设计稿「5.5寸手持端设计文件」各列表页顶部搜索区。
|
||||
*
|
||||
* XML 用法:
|
||||
* ```xml
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneSearchBar
|
||||
* android:layout_width="match_parent"
|
||||
* android:layout_height="wrap_content"
|
||||
* hint="@{`搜索ULD编号`}"
|
||||
* value="@={viewModel.uldNo}"
|
||||
* upperCase="@{true}"
|
||||
* setOnScanClickListener="@{(v)-> viewModel.scanUld()}"
|
||||
* setOnFilterClickListener="@{(v)-> activity.toggleFilterPanel()}"
|
||||
* setOnSearchListener="@{()-> viewModel.searchClick()}" />
|
||||
* ```
|
||||
*
|
||||
* 说明:与平板端 [com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout] 并列,
|
||||
* 二者互不影响;手机布局用本组件,平板布局继续用 PadSearchLayout。
|
||||
*/
|
||||
class PhoneSearchBar @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
val llSearch: LinearLayout
|
||||
val ivSearch: ImageView
|
||||
val et: EditText
|
||||
val ivScan: ImageView
|
||||
val ivFilter: ImageView
|
||||
|
||||
/** 双向绑定回调 */
|
||||
var onChangeListener: InverseBindingListener? = null
|
||||
|
||||
/** 键盘「搜索」键 / 扫码完成后触发的查询回调 */
|
||||
var searchCallBack: (() -> Unit)? = null
|
||||
|
||||
var value: String = ""
|
||||
set(v) {
|
||||
if (field == v) return
|
||||
field = v
|
||||
if (et.text.toString() != v) et.setText(v)
|
||||
onChangeListener?.onChange()
|
||||
}
|
||||
|
||||
var hint: String = ""
|
||||
set(v) {
|
||||
field = v
|
||||
et.hint = v
|
||||
}
|
||||
|
||||
/** 是否显示扫码图标(默认显示) */
|
||||
var showScan: Boolean = true
|
||||
set(v) {
|
||||
field = v
|
||||
ivScan.visibility = if (v) VISIBLE else GONE
|
||||
}
|
||||
|
||||
/** 是否显示右侧筛选按钮(默认显示) */
|
||||
var showFilter: Boolean = true
|
||||
set(v) {
|
||||
field = v
|
||||
ivFilter.visibility = if (v) VISIBLE else GONE
|
||||
}
|
||||
|
||||
/** 输入内容强制大写字母数字(运单号 / ULD 编号等场景) */
|
||||
var upperCase: Boolean = false
|
||||
set(v) {
|
||||
field = v
|
||||
if (v) et.setUpperCaseAlphanumericFilter()
|
||||
}
|
||||
|
||||
init {
|
||||
orientation = HORIZONTAL
|
||||
gravity = android.view.Gravity.CENTER_VERTICAL
|
||||
inflate(context, R.layout.layout_phone_search_bar, this)
|
||||
|
||||
llSearch = findViewById(R.id.ll_search)
|
||||
ivSearch = findViewById(R.id.iv_search)
|
||||
et = findViewById(R.id.et)
|
||||
ivScan = findViewById(R.id.iv_scan)
|
||||
ivFilter = findViewById(R.id.iv_filter)
|
||||
|
||||
et.doOnTextChanged { text, _, _, _ ->
|
||||
value = text.toString()
|
||||
}
|
||||
|
||||
// 软键盘「搜索」键触发查询
|
||||
et.setOnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
searchCallBack?.invoke()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 只保存/恢复自身状态,不下发给内部子控件。
|
||||
* 内部 et / iv_* 的 id 在每个实例中相同,交由系统按 id 恢复会跨实例串档;
|
||||
* 取值本就由 ViewModel + DataBinding 持有,无需视图级恢复。
|
||||
*/
|
||||
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchFreezeSelfOnly(container)
|
||||
}
|
||||
|
||||
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchThawSelfOnly(container)
|
||||
}
|
||||
|
||||
/** 供外部(如扫码回调)复用的图标点击入口 */
|
||||
fun setOnScanClick(listener: View.OnClickListener?) {
|
||||
ivScan.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
fun setOnFilterClick(listener: View.OnClickListener?) {
|
||||
ivFilter.setOnClickListener(listener)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.util.SparseArray
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import com.lukouguoji.module_base.R
|
||||
|
||||
/**
|
||||
* 手机版卡片统计框(5.5 寸手持端公共组件)
|
||||
*
|
||||
* 浅灰底 + 8dp 圆角,横向等宽 2~4 列,每列为「小号灰标签 + 加粗数值」,列间带竖分隔线。
|
||||
* 对应设计稿列表卡片中部的统计区(如「架子车号 / 目的港 / 装机重」)。
|
||||
*
|
||||
* XML 用法(列数由是否传 label 决定,未传的列自动隐藏):
|
||||
* ```xml
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneStatBox
|
||||
* android:layout_width="match_parent"
|
||||
* android:layout_height="wrap_content"
|
||||
* label1="@{`架子车号`}" value1="@{bean.carId}"
|
||||
* label2="@{`目的港`}" value2="@{bean.fdest}"
|
||||
* label3="@{`装机重`}" value3='@{bean.netWeight + "kg"}' />
|
||||
* ```
|
||||
*/
|
||||
class PhoneStatBox @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private val columns: List<LinearLayout>
|
||||
private val labels: List<TextView>
|
||||
private val values: List<TextView>
|
||||
|
||||
/** 分隔线(下标 0 对应第 2 列左侧,依次类推) */
|
||||
private val dividers: List<View>
|
||||
|
||||
init {
|
||||
orientation = HORIZONTAL
|
||||
setBackgroundResource(R.drawable.bg_phone_stat_box)
|
||||
val pad = (12 * resources.displayMetrics.density).toInt()
|
||||
setPadding(pad, pad, pad, pad)
|
||||
|
||||
inflate(context, R.layout.layout_phone_stat_box, this)
|
||||
|
||||
columns = listOf(
|
||||
findViewById(R.id.ll_col1),
|
||||
findViewById(R.id.ll_col2),
|
||||
findViewById(R.id.ll_col3),
|
||||
findViewById(R.id.ll_col4)
|
||||
)
|
||||
labels = listOf(
|
||||
findViewById(R.id.tv_label1),
|
||||
findViewById(R.id.tv_label2),
|
||||
findViewById(R.id.tv_label3),
|
||||
findViewById(R.id.tv_label4)
|
||||
)
|
||||
values = listOf(
|
||||
findViewById(R.id.tv_value1),
|
||||
findViewById(R.id.tv_value2),
|
||||
findViewById(R.id.tv_value3),
|
||||
findViewById(R.id.tv_value4)
|
||||
)
|
||||
dividers = listOf(
|
||||
findViewById(R.id.divider2),
|
||||
findViewById(R.id.divider3),
|
||||
findViewById(R.id.divider4)
|
||||
)
|
||||
|
||||
// 默认只展示第 1 列,其余待 label 赋值后再显示
|
||||
for (i in 1..3) setColumnVisible(i, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 只保存/恢复自身状态。列表中每个卡片都含一个本组件,内部子控件 id 相同,
|
||||
* 交由系统按 id 恢复会跨实例串档。
|
||||
*/
|
||||
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchFreezeSelfOnly(container)
|
||||
}
|
||||
|
||||
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchThawSelfOnly(container)
|
||||
}
|
||||
|
||||
/** 设置第 [index] 列(0 起)的标签,传空则隐藏该列 */
|
||||
fun setLabel(index: Int, text: String?) {
|
||||
if (index !in labels.indices) return
|
||||
labels[index].text = text.orEmpty()
|
||||
setColumnVisible(index, !text.isNullOrEmpty())
|
||||
}
|
||||
|
||||
/** 设置第 [index] 列(0 起)的数值 */
|
||||
fun setValue(index: Int, text: String?) {
|
||||
if (index !in values.indices) return
|
||||
values[index].text = text.orEmpty()
|
||||
}
|
||||
|
||||
private fun setColumnVisible(index: Int, visible: Boolean) {
|
||||
val flag = if (visible) VISIBLE else GONE
|
||||
columns[index].visibility = flag
|
||||
// 第 2~4 列连带其左侧分隔线一起显隐
|
||||
if (index in 1..3) dividers[index - 1].visibility = flag
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.util.SparseArray
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.databinding.InverseBindingListener
|
||||
import com.lukouguoji.module_base.R
|
||||
import dev.utils.app.info.KeyValue
|
||||
|
||||
/**
|
||||
* 手机版状态 Tab 条(5.5 寸手持端公共组件)
|
||||
*
|
||||
* 每个 Tab = 文字 + 可选角标数字,选中态为「深色加粗文字 + 底部 2dp 全宽指示条」。
|
||||
* 对应设计稿各列表页的「待交接 / 已交接」「全部 / 待移库 / 已移库」等状态切换栏。
|
||||
*
|
||||
* XML 用法:
|
||||
* ```xml
|
||||
* <com.lukouguoji.module_base.ui.weight.phone.PhoneStatusTab
|
||||
* android:layout_width="match_parent"
|
||||
* android:layout_height="wrap_content"
|
||||
* tabs="@{viewModel.handoverStateTabs}"
|
||||
* counts="@{viewModel.handoverStateCounts}"
|
||||
* value="@={viewModel.handoverState}"
|
||||
* setOnTabChanged="@{()-> activity.onTabChanged()}" />
|
||||
* ```
|
||||
*
|
||||
* [tabs] 用 [KeyValue]:`key` 为显示文字,`value` 为写回 [value] 的业务值。
|
||||
* [counts] 为与 tabs 等长的角标文案列表,传 null 或空串则该 Tab 不显示角标。
|
||||
*/
|
||||
class PhoneStatusTab @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
/**
|
||||
* 同一页面可能出现多个含内部 id 的手机版复合组件,系统按 id 保存/恢复视图状态会跨实例串档。
|
||||
* 这里只保存/恢复自身状态、不下发给子控件;选中值由 ViewModel + DataBinding 持有,无需视图级恢复。
|
||||
*/
|
||||
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchFreezeSelfOnly(container)
|
||||
}
|
||||
|
||||
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
|
||||
dispatchThawSelfOnly(container)
|
||||
}
|
||||
|
||||
/** 双向绑定回调 */
|
||||
var onChangeListener: InverseBindingListener? = null
|
||||
|
||||
/** Tab 切换回调(用户点击引起的变化才触发,外部赋值不触发) */
|
||||
var tabChangedCallBack: (() -> Unit)? = null
|
||||
|
||||
var tabs: List<KeyValue> = emptyList()
|
||||
set(v) {
|
||||
field = v
|
||||
buildTabs()
|
||||
}
|
||||
|
||||
var counts: List<String> = emptyList()
|
||||
set(v) {
|
||||
field = v
|
||||
refreshState()
|
||||
}
|
||||
|
||||
var value: String = ""
|
||||
set(v) {
|
||||
if (field == v) return
|
||||
field = v
|
||||
refreshState()
|
||||
onChangeListener?.onChange()
|
||||
}
|
||||
|
||||
init {
|
||||
orientation = HORIZONTAL
|
||||
setBackgroundColor(ContextCompat.getColor(context, R.color.white))
|
||||
}
|
||||
|
||||
private fun buildTabs() {
|
||||
removeAllViews()
|
||||
tabs.forEachIndexed { index, tab ->
|
||||
addView(createTab(tab, index))
|
||||
}
|
||||
refreshState()
|
||||
}
|
||||
|
||||
private fun createTab(tab: KeyValue, index: Int): View {
|
||||
val column = LinearLayout(context).apply {
|
||||
orientation = VERTICAL
|
||||
layoutParams = LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f)
|
||||
setOnClickListener {
|
||||
val target = tab.value ?: ""
|
||||
if (value != target) {
|
||||
value = target
|
||||
tabChangedCallBack?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 文字 + 角标
|
||||
val row = LinearLayout(context).apply {
|
||||
orientation = HORIZONTAL
|
||||
gravity = Gravity.CENTER
|
||||
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, dp(42))
|
||||
}
|
||||
row.addView(TextView(context).apply {
|
||||
id = titleIdOf(index)
|
||||
text = tab.key
|
||||
textSize = 14f
|
||||
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
||||
})
|
||||
row.addView(TextView(context).apply {
|
||||
id = badgeIdOf(index)
|
||||
textSize = 11f
|
||||
gravity = Gravity.CENTER
|
||||
setBackgroundResource(R.drawable.bg_phone_badge)
|
||||
setTextColor(ContextCompat.getColor(context, R.color.phone_primary))
|
||||
setPadding(dp(6), dp(1), dp(6), dp(1))
|
||||
layoutParams = LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
).apply { marginStart = dp(6) }
|
||||
})
|
||||
column.addView(row)
|
||||
|
||||
// 底部指示条
|
||||
column.addView(View(context).apply {
|
||||
id = indicatorIdOf(index)
|
||||
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, dp(2))
|
||||
})
|
||||
|
||||
return column
|
||||
}
|
||||
|
||||
private fun refreshState() {
|
||||
tabs.forEachIndexed { index, tab ->
|
||||
val selected = (tab.value ?: "") == value
|
||||
|
||||
findViewById<TextView>(titleIdOf(index))?.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
if (selected) R.color.phone_text_title else R.color.phone_text_body
|
||||
)
|
||||
)
|
||||
|
||||
findViewById<View>(indicatorIdOf(index))?.setBackgroundColor(
|
||||
if (selected) ContextCompat.getColor(context, R.color.phone_text_title)
|
||||
else android.graphics.Color.TRANSPARENT
|
||||
)
|
||||
|
||||
findViewById<TextView>(badgeIdOf(index))?.apply {
|
||||
val count = counts.getOrNull(index).orEmpty()
|
||||
if (count.isEmpty()) {
|
||||
visibility = GONE
|
||||
} else {
|
||||
visibility = VISIBLE
|
||||
text = count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun dp(value: Int): Int =
|
||||
(value * resources.displayMetrics.density).toInt()
|
||||
|
||||
// 动态生成的子 View 用固定偏移量作为 id,避免与资源 id 冲突
|
||||
private fun titleIdOf(index: Int) = ID_BASE_TITLE + index
|
||||
private fun badgeIdOf(index: Int) = ID_BASE_BADGE + index
|
||||
private fun indicatorIdOf(index: Int) = ID_BASE_INDICATOR + index
|
||||
|
||||
companion object {
|
||||
private const val ID_BASE_TITLE = 0x7E100000
|
||||
private const val ID_BASE_BADGE = 0x7E200000
|
||||
private const val ID_BASE_INDICATOR = 0x7E300000
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
package com.lukouguoji.module_base.ui.weight.phone
|
||||
|
||||
import android.view.View
|
||||
import androidx.databinding.BindingAdapter
|
||||
import androidx.databinding.InverseBindingAdapter
|
||||
import androidx.databinding.InverseBindingListener
|
||||
import dev.utils.app.info.KeyValue
|
||||
|
||||
/**
|
||||
* 5.5 寸手持端公共组件的 DataBinding 适配器集合
|
||||
*
|
||||
* 所有属性均为无命名空间写法(与平板端 PadSearchLayout / PadDataLayoutNew 的用法一致)。
|
||||
* 同名属性(hint / value / title / list 等)按控件类型解析,与平板端组件互不冲突。
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// PhoneSearchBar
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@BindingAdapter("hint", "showScan", "showFilter", "upperCase", requireAll = false)
|
||||
fun setPhoneSearchBarAttrs(
|
||||
bar: PhoneSearchBar,
|
||||
hint: String?,
|
||||
showScan: Boolean?,
|
||||
showFilter: Boolean?,
|
||||
upperCase: Boolean?
|
||||
) {
|
||||
hint?.let { bar.hint = it }
|
||||
showScan?.let { bar.showScan = it }
|
||||
showFilter?.let { bar.showFilter = it }
|
||||
upperCase?.let { bar.upperCase = it }
|
||||
}
|
||||
|
||||
@BindingAdapter("value", requireAll = false)
|
||||
fun setPhoneSearchBarValue(bar: PhoneSearchBar, value: String?) {
|
||||
value?.let { bar.value = it }
|
||||
}
|
||||
|
||||
@InverseBindingAdapter(attribute = "value", event = "valueAttrChanged")
|
||||
fun getPhoneSearchBarValue(bar: PhoneSearchBar): String = bar.value
|
||||
|
||||
@BindingAdapter("valueAttrChanged", requireAll = false)
|
||||
fun setPhoneSearchBarValueAttrChanged(bar: PhoneSearchBar, listener: InverseBindingListener) {
|
||||
bar.onChangeListener = listener
|
||||
}
|
||||
|
||||
@BindingAdapter("setOnScanClickListener", requireAll = false)
|
||||
fun setPhoneSearchBarScanClick(bar: PhoneSearchBar, listener: View.OnClickListener?) {
|
||||
bar.setOnScanClick(listener)
|
||||
}
|
||||
|
||||
@BindingAdapter("setOnFilterClickListener", requireAll = false)
|
||||
fun setPhoneSearchBarFilterClick(bar: PhoneSearchBar, listener: View.OnClickListener?) {
|
||||
bar.setOnFilterClick(listener)
|
||||
}
|
||||
|
||||
@BindingAdapter("setRefreshCallBack", requireAll = false)
|
||||
fun setPhoneSearchBarRefreshCallBack(bar: PhoneSearchBar, callBack: (() -> Unit)?) {
|
||||
bar.searchCallBack = callBack
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// PhoneStatusTab
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@BindingAdapter("tabs", requireAll = false)
|
||||
fun setPhoneStatusTabTabs(tab: PhoneStatusTab, tabs: List<KeyValue>?) {
|
||||
tabs?.let { tab.tabs = it }
|
||||
}
|
||||
|
||||
@BindingAdapter("counts", requireAll = false)
|
||||
fun setPhoneStatusTabCounts(tab: PhoneStatusTab, counts: List<String>?) {
|
||||
tab.counts = counts ?: emptyList()
|
||||
}
|
||||
|
||||
@BindingAdapter("value", requireAll = false)
|
||||
fun setPhoneStatusTabValue(tab: PhoneStatusTab, value: String?) {
|
||||
value?.let { tab.value = it }
|
||||
}
|
||||
|
||||
@InverseBindingAdapter(attribute = "value", event = "valueAttrChanged")
|
||||
fun getPhoneStatusTabValue(tab: PhoneStatusTab): String = tab.value
|
||||
|
||||
@BindingAdapter("valueAttrChanged", requireAll = false)
|
||||
fun setPhoneStatusTabValueAttrChanged(tab: PhoneStatusTab, listener: InverseBindingListener) {
|
||||
tab.onChangeListener = listener
|
||||
}
|
||||
|
||||
@BindingAdapter("setOnTabChanged", requireAll = false)
|
||||
fun setPhoneStatusTabChanged(tab: PhoneStatusTab, callBack: (() -> Unit)?) {
|
||||
tab.tabChangedCallBack = callBack
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// PhoneDataLayout
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@BindingAdapter("title", "hint", "type", "required", "enable", requireAll = false)
|
||||
fun setPhoneDataLayoutAttrs(
|
||||
layout: PhoneDataLayout,
|
||||
title: String?,
|
||||
hint: String?,
|
||||
type: PhoneDataLayoutType?,
|
||||
required: Boolean?,
|
||||
enable: Boolean?
|
||||
) {
|
||||
// type 必须先于 value/hint 生效,否则控件形态与取值分支不匹配
|
||||
type?.let { layout.type = it }
|
||||
title?.let { layout.title = it }
|
||||
hint?.let { layout.hint = it }
|
||||
required?.let { layout.required = it }
|
||||
enable?.let { layout.enable = it }
|
||||
}
|
||||
|
||||
@BindingAdapter("value", requireAll = false)
|
||||
fun setPhoneDataLayoutValue(layout: PhoneDataLayout, value: String?) {
|
||||
value?.let { layout.value = it }
|
||||
}
|
||||
|
||||
@InverseBindingAdapter(attribute = "value", event = "valueAttrChanged")
|
||||
fun getPhoneDataLayoutValue(layout: PhoneDataLayout): String = layout.value
|
||||
|
||||
@BindingAdapter("valueAttrChanged", requireAll = false)
|
||||
fun setPhoneDataLayoutValueAttrChanged(
|
||||
layout: PhoneDataLayout,
|
||||
listener: InverseBindingListener
|
||||
) {
|
||||
layout.onChangeListener = listener
|
||||
}
|
||||
|
||||
@BindingAdapter("list", requireAll = false)
|
||||
fun setPhoneDataLayoutList(layout: PhoneDataLayout, list: List<KeyValue>?) {
|
||||
list?.let { layout.list = it }
|
||||
}
|
||||
|
||||
@BindingAdapter("setRefreshCallBack", requireAll = false)
|
||||
fun setPhoneDataLayoutRefreshCallBack(layout: PhoneDataLayout, callBack: (() -> Unit)?) {
|
||||
layout.refreshCallBack = callBack
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// PhoneFilterPanel
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@BindingAdapter("title", "panelVisible", requireAll = false)
|
||||
fun setPhoneFilterPanelAttrs(panel: PhoneFilterPanel, title: String?, panelVisible: Boolean?) {
|
||||
title?.let { panel.title = it }
|
||||
panelVisible?.let { panel.panelVisible = it }
|
||||
}
|
||||
|
||||
@BindingAdapter("setOnResetClick", requireAll = false)
|
||||
fun setPhoneFilterPanelResetClick(panel: PhoneFilterPanel, listener: View.OnClickListener?) {
|
||||
panel.tvReset.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
@BindingAdapter("setOnConfirmClick", requireAll = false)
|
||||
fun setPhoneFilterPanelConfirmClick(panel: PhoneFilterPanel, listener: View.OnClickListener?) {
|
||||
panel.tvConfirm.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
@BindingAdapter("setOnDismissClick", requireAll = false)
|
||||
fun setPhoneFilterPanelDismissClick(panel: PhoneFilterPanel, listener: View.OnClickListener?) {
|
||||
panel.setOnDismiss(listener)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// PhoneBottomBar
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@BindingAdapter("allChecked", "countText", "actionText", requireAll = false)
|
||||
fun setPhoneBottomBarAttrs(
|
||||
bar: PhoneBottomBar,
|
||||
allChecked: Boolean?,
|
||||
countText: String?,
|
||||
actionText: String?
|
||||
) {
|
||||
allChecked?.let { bar.allChecked = it }
|
||||
countText?.let { bar.countText = it }
|
||||
actionText?.let { bar.actionText = it }
|
||||
}
|
||||
|
||||
@BindingAdapter("setOnAllCheckClick", requireAll = false)
|
||||
fun setPhoneBottomBarAllCheckClick(bar: PhoneBottomBar, listener: View.OnClickListener?) {
|
||||
bar.llCheckAll.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
@BindingAdapter("setOnActionClick", requireAll = false)
|
||||
fun setPhoneBottomBarActionClick(bar: PhoneBottomBar, listener: View.OnClickListener?) {
|
||||
bar.tvAction.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// PhoneStatBox
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@BindingAdapter(
|
||||
"label1", "value1", "label2", "value2",
|
||||
"label3", "value3", "label4", "value4",
|
||||
requireAll = false
|
||||
)
|
||||
fun setPhoneStatBoxAttrs(
|
||||
box: PhoneStatBox,
|
||||
label1: String?, value1: String?,
|
||||
label2: String?, value2: String?,
|
||||
label3: String?, value3: String?,
|
||||
label4: String?, value4: String?
|
||||
) {
|
||||
box.setLabel(0, label1)
|
||||
box.setValue(0, value1)
|
||||
box.setLabel(1, label2)
|
||||
box.setValue(1, value2)
|
||||
box.setLabel(2, label3)
|
||||
box.setValue(2, value3)
|
||||
box.setLabel(3, label4)
|
||||
box.setValue(3, value4)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// PhoneKvItem
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@BindingAdapter("title", "value", "tag", "tagColor", requireAll = false)
|
||||
fun setPhoneKvItemAttrs(
|
||||
item: PhoneKvItem,
|
||||
title: String?,
|
||||
value: String?,
|
||||
tag: Boolean?,
|
||||
tagColor: String?
|
||||
) {
|
||||
title?.let { item.title = it }
|
||||
// tag/tagColor 需先于 value 生效,确保样式切换后再填内容
|
||||
tagColor?.let { item.tagColor = it }
|
||||
tag?.let { item.tag = it }
|
||||
item.value = value.orEmpty()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// PhoneFormRow
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@BindingAdapter("title", "hint", "type", "required", "enable", "numeric", requireAll = false)
|
||||
fun setPhoneFormRowAttrs(
|
||||
row: PhoneFormRow,
|
||||
title: String?,
|
||||
hint: String?,
|
||||
type: PhoneFormRowType?,
|
||||
required: Boolean?,
|
||||
enable: Boolean?,
|
||||
numeric: Boolean?
|
||||
) {
|
||||
// type 先于其余属性生效,确保 value/hint 写进正确的子控件
|
||||
type?.let { row.type = it }
|
||||
title?.let { row.title = it }
|
||||
hint?.let { row.hint = it }
|
||||
required?.let { row.required = it }
|
||||
enable?.let { row.enable = it }
|
||||
numeric?.let { row.numeric = it }
|
||||
}
|
||||
|
||||
@BindingAdapter("value", requireAll = false)
|
||||
fun setPhoneFormRowValue(row: PhoneFormRow, value: String?) {
|
||||
if (row.value != value.orEmpty()) row.value = value.orEmpty()
|
||||
}
|
||||
|
||||
@InverseBindingAdapter(attribute = "value", event = "valueAttrChanged")
|
||||
fun getPhoneFormRowValue(row: PhoneFormRow): String = row.value
|
||||
|
||||
@BindingAdapter("valueAttrChanged", requireAll = false)
|
||||
fun setPhoneFormRowValueAttrChanged(row: PhoneFormRow, listener: InverseBindingListener) {
|
||||
row.onChangeListener = listener
|
||||
}
|
||||
|
||||
@BindingAdapter("list", requireAll = false)
|
||||
fun setPhoneFormRowList(row: PhoneFormRow, list: List<KeyValue>?) {
|
||||
row.list = list ?: emptyList()
|
||||
}
|
||||
|
||||
@BindingAdapter("setRefreshCallBack", requireAll = false)
|
||||
fun setPhoneFormRowRefreshCallBack(row: PhoneFormRow, callBack: (() -> Unit)?) {
|
||||
row.refreshCallBack = callBack
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// PhoneBottomBar(追加:危险操作样式)
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@BindingAdapter("actionDanger", requireAll = false)
|
||||
fun setPhoneBottomBarActionDanger(bar: PhoneBottomBar, danger: Boolean?) {
|
||||
danger?.let { bar.actionDanger = it }
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.lukouguoji.module_base.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.LayoutRes
|
||||
import com.lukouguoji.module_base.MyApplication
|
||||
|
||||
/**
|
||||
* 设备形态判定工具
|
||||
*
|
||||
* 用于「平板(10 寸横屏)」与「手机(5.5 寸竖屏)」双形态适配:
|
||||
* 1. 识别当前设备是平板还是手机
|
||||
* 2. 提供按形态选择布局的入口(Activity 布局 / RecyclerView item 布局通用)
|
||||
* 3. 提供手机端 AutoSize 设计基准尺寸常量
|
||||
*
|
||||
* 判定依据:`smallestScreenWidthDp`(最小屏幕宽度,与横竖屏无关,是 Android 官方推荐的设备分类维度)
|
||||
* - 5.5 寸手机 ≈ 360dp
|
||||
* - 10 寸平板 ≈ 800dp
|
||||
* - 分界线取官方标准 600dp
|
||||
*
|
||||
* 注意:这里读取的是 [Resources.getSystem](系统资源)而非应用 Resources,
|
||||
* 目的是绕开 AndroidAutoSize 对 density 的动态改写,保证判定结果稳定。
|
||||
*/
|
||||
object DeviceUtil {
|
||||
|
||||
/** 平板判定阈值:最小屏幕宽度 >= 600dp 视为平板 */
|
||||
private const val TABLET_MIN_WIDTH_DP = 600
|
||||
|
||||
/** 手机端设计稿基准尺寸(对应 5.5 寸手持端设计稿画布 390 × 844) */
|
||||
const val PHONE_DESIGN_WIDTH_DP = 390
|
||||
const val PHONE_DESIGN_HEIGHT_DP = 844
|
||||
|
||||
/** 平板端设计稿基准尺寸(沿用现有配置) */
|
||||
const val TABLET_DESIGN_WIDTH_DP_LAND = 1152
|
||||
const val TABLET_DESIGN_HEIGHT_DP_LAND = 720
|
||||
const val TABLET_DESIGN_WIDTH_DP_PORT = 720
|
||||
const val TABLET_DESIGN_HEIGHT_DP_PORT = 1280
|
||||
|
||||
/**
|
||||
* 调试用:强制指定设备形态
|
||||
*
|
||||
* 开发阶段可在平板上强制走手机布局做验证,正式发布保持 [ForceMode.AUTO]。
|
||||
* 用法:`DeviceUtil.forceMode = DeviceUtil.ForceMode.PHONE`
|
||||
*/
|
||||
enum class ForceMode { AUTO, PHONE, TABLET }
|
||||
|
||||
@JvmStatic
|
||||
var forceMode: ForceMode = ForceMode.AUTO
|
||||
|
||||
/** 缓存自动判定结果,避免每次都读 Configuration */
|
||||
private var cachedIsTablet: Boolean? = null
|
||||
|
||||
/**
|
||||
* 是否平板
|
||||
* @param context 可不传,默认用 Application 上下文(判定与具体 Activity 无关)
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun isTablet(context: Context? = null): Boolean = when (forceMode) {
|
||||
ForceMode.PHONE -> false
|
||||
ForceMode.TABLET -> true
|
||||
ForceMode.AUTO -> cachedIsTablet ?: (smallestWidthDp(context) >= TABLET_MIN_WIDTH_DP)
|
||||
.also { cachedIsTablet = it }
|
||||
}
|
||||
|
||||
/** 是否手机 */
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun isPhone(context: Context? = null): Boolean = !isTablet(context)
|
||||
|
||||
/**
|
||||
* 获取最小屏幕宽度(dp)
|
||||
*
|
||||
* 优先读系统资源(不受 AutoSize 影响);异常时回退到应用资源。
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun smallestWidthDp(context: Context? = null): Int {
|
||||
val fromSystem = Resources.getSystem()?.configuration?.smallestScreenWidthDp ?: 0
|
||||
if (fromSystem > 0) return fromSystem
|
||||
|
||||
val res = (context ?: runCatching { MyApplication.context }.getOrNull())?.resources
|
||||
return res?.configuration?.smallestScreenWidthDp ?: TABLET_MIN_WIDTH_DP
|
||||
}
|
||||
|
||||
/**
|
||||
* 按设备形态选择布局
|
||||
*
|
||||
* @param tabletLayout 平板布局(现有布局,必填)
|
||||
* @param phoneLayout 手机布局;传 0 或未提供时,手机上回退使用平板布局
|
||||
* @return 当前设备应加载的布局 id
|
||||
*
|
||||
* 用法(ViewModel 中选择 item 布局):
|
||||
* ```
|
||||
* val itemLayoutId = DeviceUtil.layout(R.layout.item_xxx, R.layout.item_xxx_phone)
|
||||
* ```
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun layout(
|
||||
@LayoutRes tabletLayout: Int,
|
||||
@LayoutRes phoneLayout: Int = 0,
|
||||
context: Context? = null
|
||||
): Int = if (phoneLayout != 0 && isPhone(context)) phoneLayout else tabletLayout
|
||||
|
||||
/** 配置发生变化时清空缓存(如分屏、折叠屏形态切换) */
|
||||
@JvmStatic
|
||||
fun clearCache() {
|
||||
cachedIsTablet = null
|
||||
}
|
||||
|
||||
/** 设备形态描述,用于日志排查 */
|
||||
@JvmStatic
|
||||
fun describe(): String =
|
||||
"${if (isTablet()) "TABLET" else "PHONE"}(sw=${smallestWidthDp()}dp, force=$forceMode)"
|
||||
}
|
||||
@@ -1,46 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.lukouguoji.module_base">
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/logot"
|
||||
android:roundIcon="@mipmap/logot_round">
|
||||
<activity
|
||||
android:name=".ui.signature.SignatureActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="false"
|
||||
android:screenOrientation="userLandscape" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="true"
|
||||
android:screenOrientation="userLandscape" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.page.preview.PreviewActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="true"/>
|
||||
|
||||
<!-- PDF预览Activity -->
|
||||
<activity
|
||||
android:name=".ui.page.preview.PdfPreviewActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="false"
|
||||
android:screenOrientation="userLandscape" />
|
||||
|
||||
<!-- 屏幕适配 -->
|
||||
<meta-data
|
||||
android:name="design_width_in_dp"
|
||||
android:value="1152" />
|
||||
<meta-data
|
||||
android:name="design_height_in_dp"
|
||||
android:value="720" />
|
||||
</application>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.lukouguoji.module_base">
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/logot"
|
||||
android:roundIcon="@mipmap/logot_round">
|
||||
<activity
|
||||
android:name=".ui.signature.SignatureActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="false"
|
||||
android:screenOrientation="unspecified" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="true"
|
||||
android:screenOrientation="unspecified" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.page.preview.PreviewActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="true"/>
|
||||
|
||||
<!-- PDF预览Activity -->
|
||||
<activity
|
||||
android:name=".ui.page.preview.PdfPreviewActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="false"
|
||||
android:screenOrientation="unspecified" />
|
||||
|
||||
<!-- 屏幕适配 -->
|
||||
<meta-data
|
||||
android:name="design_width_in_dp"
|
||||
android:value="1152" />
|
||||
<meta-data
|
||||
android:name="design_height_in_dp"
|
||||
android:value="720" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user