feat: 完成国际出港移库/出库交接页面5.5寸手机端适配

- 落地手机端双形态适配框架:同名布局+资源限定符(layout/手机、
  layout-sw600dp/平板)自动切换,DeviceUtil 设备形态判定,7个
  手机版公共组件(PhoneSearchBar/StatusTab/DataLayout/FilterPanel/
  BottomBar/StatBox/KvItem)
- 完成「国际出港移库」「国际出港出库交接」两页手机端适配,新增
  各自详情页(IntExpMoveDetailActivity/IntExpOutHandoverDetailActivity)
- 手机端首页菜单接入"出港移库""出库交接"入口
- 修复手机端进入页面先横屏后转竖屏的闪屏问题:Manifest 全项目
  192 处改为 screenOrientation="unspecified",由 BaseActivity
  按设备形态运行时锁定方向
- 修复该方案的中间版本在平板端引入的回归(先竖后横 + UI放大1.6倍)
- AutoSize 补充手机竖屏 390×844 设计基准
- 修复 CHANGELOG.md 因脚本异常导致的内容重复损坏(膨胀至12万行),
  恢复正常结构并补充本次变更记录

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 14:18:29 +08:00
parent 9c4588e47e
commit db062c8ee3
134 changed files with 19602 additions and 2965 deletions

View 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