diff --git a/.agents/docs/plans/MCP化改造方案-2026-10-19.md b/.agents/docs/plans/MCP化改造方案-2026-10-19.md index d70531d..13b2627 100644 --- a/.agents/docs/plans/MCP化改造方案-2026-10-19.md +++ b/.agents/docs/plans/MCP化改造方案-2026-10-19.md @@ -696,3 +696,15 @@ capture 只出现在 camera 段、脚本里不许再出现 `setAttribute`、两 教训:这次不是代码写错,是**我按规范写的属性被某个浏览器解释成另一种意思**。移动端表单的能力边界 以真机为准,能用最保守的写法就别用"更聪明"的写法。 + +## 22. 按钮名字别再承诺浏览器给不了的东西(2026-10-21) + +去掉 accept 之后手机能选到文件了,但用户实测那个入口开的是**系统文件选择器**,不是相册网格: +「从相册或文件选择」这个名字是我按规范许的愿,设备不给。用户接受"只有文件也行,照片在里面能找到", +要求把名字改实:按钮改为「**选择已有文件**」,顶部说明改成「拍一张,或从手机里选已有的文件」, +并在按钮下补一句照片通常在 图片/DCIM 目录里能找到。 + +测试里加了一条断言 `assert "从相册" not in html`——它不是格式检查,是**防止以后又有人把承诺写回文案**。 + +同类教训连着三条(capture 挂错、accept 被路由到相机、按钮名过度承诺)都指向一件事: +移动端网页的能力边界由设备与 WebView 决定,文案只能描述**实测到的行为**,不能描述规范里的应然。 diff --git a/docs/报销操作指南.md b/docs/报销操作指南.md index 3db375a..c49fcf9 100644 --- a/docs/报销操作指南.md +++ b/docs/报销操作指南.md @@ -291,6 +291,7 @@ URL 类字段(`sso_login_url`、`portal_url`、`reimburse_url`、`reimburse_pa | 手机只能拍照、选不了相册和已有文件 | 两代原因:`capture` 挂到了唯一 input;相册入口带 `accept` 被微信内置浏览器/ 部分国产 ROM 直接路由到相机。都已修:相册入口不带 accept,类型交给服务端白名单 | | 改了却像没生效 | 手机侧重开上传页;PC 侧开 `/api/ping` 看是不是旧进程在服务,重启收件器加载新代码 | +| 「选择已有文件」里没有相册页签 | 各手机给的文件选择器不同;照片在里面能翻到(多在 图片 / DCIM / Download 目录),或者直接用「拍照上传」 | | 改了收件器页面却"没生效" | 多半是旧的孤儿进程还在听 5055。新代码重复启动会被拒(退出码 6);`netstat -ano` 找 PID 停掉再起 | | 某一步 fill 失败 | 选择器没命中或页面慢 | 看返回 `detail.screenshot` 的截图,人工看一眼再重填该步 | diff --git a/src/upload/README.md b/src/upload/README.md index 9bcd022..4c81406 100644 --- a/src/upload/README.md +++ b/src/upload/README.md @@ -51,7 +51,7 @@ uv run python -m src.upload.server --port 6000 --pin 1234 | 入口 | input 属性 | 为什么这样写 | |------|-----------|-------------| | 拍照上传 | `accept="image/*" capture="environment"` | capture 只能挂在这一个上;挂到唯一 input 就等于只能拍照 | -| 从相册或文件选择 | 只有 `multiple`,**没有 accept** | 一旦带 `accept="image/*"`,微信内置浏览器与部分国产 ROM 会直接调起相机 +| 选择已有文件 | 只有 `multiple`,**没有 accept** | 一旦带 `accept="image/*"`,微信内置浏览器与部分国产 ROM 会直接调起相机 (用户实测)。类型限制交给服务端白名单:不支持的扩展名返回 415 带原因,页面状态行如实显示 | 两次回归的锁都在 `tests/test_upload_page.py`:capture 归属、gallery 段无 accept、不出现 setAttribute 与 `.click()`。 diff --git a/src/upload/page.py b/src/upload/page.py index 3a0222c..b94f5b5 100644 --- a/src/upload/page.py +++ b/src/upload/page.py @@ -39,14 +39,15 @@ HEAD = ( ) MOBILE_BODY = """ -

__CASE_NAME__

拍照或从相册选择发票、支付截图

+

__CASE_NAME__

拍一张,或从手机里选已有的文件

- +
+

「选择已有文件」走系统文件选择器,相册里的照片也能在里面找到(一般在图片/DCIM 目录)。

收件箱

上传后回到电脑,对 Agent 说「收材料」即可。文件先落收件箱,不会自动进入报销流程。

diff --git a/tests/test_upload_page.py b/tests/test_upload_page.py index eaad1ae..3d20b6c 100644 --- a/tests/test_upload_page.py +++ b/tests/test_upload_page.py @@ -13,6 +13,10 @@ def test_mobile_page_offers_two_pickers() -> None: html = mobile_page("6月毕业采集", "20261021-case", "tok123") assert html.count('type="file"') == 2 assert 'for="camera"' in html and 'for="gallery"' in html # 用 label 原生转发,不靠 JS .click() + assert "拍照上传" in html and "选择已有文件" in html + # 文案只描述实测行为:那个入口开的是系统文件选择器,不是相册网格。 + # 这条断言防的是以后有人把「相册」写回按钮名(用户实测进不去相册)。 + assert "从相册" not in html and "相册或文件" not in html def test_capture_only_on_camera_input() -> None: diff --git a/tests/test_upload_server.py b/tests/test_upload_server.py index 523ae9e..d93f159 100644 --- a/tests/test_upload_server.py +++ b/tests/test_upload_server.py @@ -43,4 +43,4 @@ def test_mobile_pages_two_entry_points_in_served_html() -> None: gallery = html.split('id="gallery"', 1)[1].split(">", 1)[0] assert 'capture="environment"' in camera and "capture" not in gallery assert html.count('type="file"') == 2 - assert "拍照上传" in html and "从相册或文件选择" in html + assert "拍照上传" in html and "选择已有文件" in html