diff --git a/bak/mobile-ui/shanxia-mp-pl-plan-webview-demo.html b/bak/mobile-ui/shanxia-mp-pl-plan-webview-demo.html new file mode 100644 index 000000000..cc20b7d3a --- /dev/null +++ b/bak/mobile-ui/shanxia-mp-pl-plan-webview-demo.html @@ -0,0 +1,563 @@ + + + + + + 山峡化工 · 运输计划(WebView H5 Demo) + + + +
+ + +
+ +
+
① 列表页(默认)
+
+
09:41WebView
+
+
+ ← 工作台 + +
+

运输计划

+
登录态由小程序注入 Token · H5 调同一套 /api
+ +
+ +
+ + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + +
+ +
+ +
+ +
+ + +
+
+
+ + +
+
② 详情 / 操作(点卡片进入)
+
+
09:41WebView
+
+
+ ← 列表 + 待安排 +
+

JH20260914003

+
出库单 · 计划日期 2026-09-14
+
+ +
+
+

基本信息

+
跟进状态待安排
+
送货时间09-14 14:00
+
车牌号皖H·D1234 / 挂 皖H·G88
+
人员司机 张伟 · 押运 李强
+
跟进人王调度
+
+ +
+

产品摘要

+
+ 甲醇 32 吨 · 包装吨袋
+ 供方:安庆山峡 · 需方:合肥某化 +
+
移动端以卡片摘要为主;完整编辑走「编辑计划」全屏表单(同管理端字段,分步/分区展示)。
+
+ +
+

快捷操作(对齐管理端工具栏)

+
+ + + + + + +
+
+
+ +
+ + +
+
+
+
+ +
+ 请确认 Demo 方向(回复编号即可) +
    +
  1. A — 列表用「卡片流」如左图(推荐,适合手机一屏信息)
  2. +
  3. B — 列表尽量仿桌面表格(横滑多列,信息更全但操作更重)
  4. +
  5. C — 先只做列表+查看+安排运输,其余打印/作废/三方人车二期再上
  6. +
+ 也可直接说要改哪里(Tab、按钮优先级、字段展示等)。 +
+
+ + + + diff --git a/docs/superpowers/specs/2026-09-14-mp-webview-transport-plan-design.md b/docs/superpowers/specs/2026-09-14-mp-webview-transport-plan-design.md new file mode 100644 index 000000000..9e845c91c --- /dev/null +++ b/docs/superpowers/specs/2026-09-14-mp-webview-transport-plan-design.md @@ -0,0 +1,229 @@ +# 小程序 WebView 嵌入模板 + 运输计划首发 — 设计文档 + +> **日期**: 2026-09-14 +> **状态**: 待评审 +> **方案**: 小程序通用 `web-view` 壳 + `web-admin` 移动端路由 `/#/m/*`;首个业务为管理端「运输计划」全量能力 +> **Demo**: `bak/mobile-ui/shanxia-mp-pl-plan-webview-demo.html`(卡片流列表,已选方案 A) + +## 1. 目标 + +在微信小程序中提供与 Web 管理端 **运输管理 → 运输计划**(`/#/pl/plan-module/plan`)同等业务能力,并以 **WebView 加载 H5** 落地,使业务改动只发门户静态资源,不必每次提审小程序。 + +本次同时沉淀 **可复用模板**:后续其它管理端页面按同一骨架接入,不再复制鉴权、壳页与布局。 + +## 2. 已确认决策 + +| # | 项 | 决策 | +|---|-----|------| +| D1 | 列表形态 | 卡片流(Demo A),不做桌面表格横滑 | +| D2 | 功能范围 | **全量对齐**管理端:新增/编辑、安排运输、复制、等通知、打印、作废、三方人车快速创建 | +| D3 | H5 落点 | 在 `web-admin` 新增 `/#/m/...` 移动端路由,复用现有 API 与业务逻辑;不为 WebView 新建独立工程 | +| D4 | 登录态 | **必须使用小程序已登录用户的 JWT**;H5 禁止再走 `/auth/login` | +| D5 | Token 传递 | **一次性短时 ticket** 换票;URL **不得**长期携带 accessToken | +| D6 | 模板化 | 小程序只维护一个通用 WebView 壳;后续模块只加 H5 路由 + 工作台入口 | +| D7 | 与现网小程序页 | **并存**:现有 `pages/pl/transport-plan/*`(现场记录/现场回传)不替换、不改名混用 | +| D8 | 视觉 | WebView 内 H5 使用小程序三墙 Token(`#0a9f6d` 等),**不**套管理端 Fresh Mint 侧栏/WorkTab;这是「嵌在小程序里的 H5」,不是管理端皮肤 | +| D9 | 打印 | WebView 内先做单据 **预览 / 存图**;不承诺调用手机系统打印机或 Lodop | + +## 3. 非目标 + +- 不把桌面 `el-table` / `el-dialog` 原样塞进 WebView +- 不改运输计划后端业务规则、状态机、租户隔离与数据权限 +- 不下线现场人员原生「运输计划单」与公开 H5 免登链接 +- 不在本变更中把其它模块(入库、加油、CRM 等)迁入 WebView(只定模板,首发仅运输计划) +- 不把 ticket 或 JWT 写入可被中间日志长期保留的 query(使用 hash 片段携带 ticket) + +## 4. 架构(模板) + +```text +小程序工作台 + → pages/common/mp-webview?path=/m/pl/plan + 1) POST /api/web/auth/mp-webview-ticket (小程序 Bearer) + 2) web-view src = {WEB_ORIGIN}/#/m/pl/plan?mpTicket={ticket} + (Hash 路由下 query 在 # 之后,不会进 Nginx 的 path/query 日志;见 §5) + 3) H5 启动:POST /api/web/auth/mp-webview-exchange + → accessToken + refreshToken 写入 web-admin userStore + 4) 之后与管理端相同:Authorization: Bearer + 现有 /api/transport/plan/... + +后续模块 + → 同一壳页 ?path=/m/ + → 只需新增 web-admin 页面,不必再提审小程序 +``` + +```text +┌─────────────┐ ticket ┌──────────────────┐ JWT ┌────────────┐ +│ miniprogram│ ──────────────► │ web-admin /#/m/* │ ───────────► │ FastAPI │ +│ web-view壳 │ ◄────────────── │ MpWebviewShell │ ◄─────────── │ 既有计划API │ +└─────────────┘ 401→reLaunch └──────────────────┘ 租户/权限 └────────────┘ +``` + +**边界** + +| 单元 | 职责 | 依赖 | +|------|------|------| +| `pages/common/mp-webview` | 取票、拼 URL、承载 ``、接收 H5 回登录指令 | 小程序 token、`WEB_ORIGIN`、业务域名白名单 | +| `MpWebviewShell` | 无侧栏布局、安全区、401 桥接、禁止桌面壳 | 换票结果、`wx.miniProgram` | +| `/#/m/pl/plan*` | 运输计划移动端 UI | 现有 `planApi` / composable / 校验 | +| ticket API | 签发与一次性兑换 | Redis + 当前用户 session | + +微信后台 **业务域名** 配置一次:`w.shanxiachem.com`(测试环境另配对应 IP/域名)。开发者工具本地联调使用 `WEB_ORIGIN`(如 `127.0.0.1:3006`),需在开发者工具中关闭/放宽域名校验。 + +## 5. 登录与 Token + +### 5.1 为何不能直接复用 Web 登录 + +`web-view` 是独立浏览上下文:读不到 `uni.setStorageSync`,H5 的 `localStorage` 初始为空。两端虽都走 `/web/auth/login` 发的同一套 JWT,但 **不会自动共享**。 + +禁止:H5 弹出管理端登录页;禁止 URL 明文长期挂 `accessToken`。 + +### 5.2 Ticket 协议 + +**签发** `POST /api/web/auth/mp-webview-ticket` + +- 鉴权:小程序当前 `Authorization: Bearer` +- 成功:`{ ticket, expiresIn: 60 }` +- Redis:`mp_wv_ticket:{ticket}` → `{ userId, sessionId, refreshJti? }`,TTL **60 秒**,读后删除(一次性) + +**兑换** `POST /api/web/auth/mp-webview-exchange` body `{ ticket }` + +- 无 Bearer(ticket 即凭证) +- 校验 Redis 后 **复用该用户当前 session** 签发一对新的 access/refresh(或返回与小程序相同 session 下的新 access jti) +- 成功体与登录接口对齐:`token`、`refreshToken`、`expiresIn`、`userInfo` +- 失败:401,H5 通知小程序壳 `reLaunch` 登录页 + +**加密排除**:`mp-webview-exchange` 须加入 `API_ENCRYPT_EXCLUDE_PATHS`(与 login 同类,此时 H5 尚未持有会话密钥)。签发接口走已登录加密通道。 + +**URL 形态(约定)** + +Hash 路由下 ticket 放在 hash 内,避免出现在 Nginx access log 的 path/query: + +```text +https://w.shanxiachem.com/#/m/pl/plan?mpTicket= +``` + +实现时 H5 启动立刻 `replace` 掉 `mpTicket`,只留干净路由。若必须进一步降低泄露,可将 ticket 仅通过 `wx.miniProgram.postMessage` 不可行(加载前 H5 收不到);因此短 TTL + 一次性是主要防护。 + +### 5.3 路由守卫(关键) + +`/#/m/*` **不能**走现有「未登录 → `/auth/login`」逻辑,也 **不能**在换票后走桌面动态菜单注册(会挂侧栏、WorkTab)。 + +约定 `meta.isMpWebview: true`: + +1. 允许无 `userStore.isLogin` 进入页面 +2. 页面内完成 exchange → `setLoginInfo`(或等价写入 token) +3. **跳过** `handleDynamicRoutes` 的管理端菜单注入 +4. 401 / ticket 失败 → `wx.miniProgram.reLaunch({ url: '/pages/login/login' })`,永不 `next({ name: 'Login' })` + +### 5.4 与「司机/现场免登 token」的区别 + +`driver_access_token` / `site_access_token` 是单据级公开链接,**禁止**用于本模板。本模板始终是员工登录态。 + +## 6. 权限与菜单 + +### 6.1 小程序工作台(入口) + +现网「运输计划单」perms = `mini:pl:transportPlan:list`,对应 **现场回传**,保持不变。 + +新增菜单(名称与管理端对齐,避免再叫「运输计划单」): + +| 字段 | 值 | +|------|-----| +| menu_name | `运输计划` | +| path | `mp-webview` 或工作台 code `pl-plan-webview` | +| perms | `mini:pl:plan:manage` | +| 父级 | 运输管理(`MINI_PL_ROOT`) | +| order_num | 排在现有「运输计划单」之后,避免打乱现场人员入口顺序 | + +工作台配置:`enabled` + `code` → 打开通用壳 `PAGE_PATHS.mpWebview + '?path=' + encodeURIComponent('/m/pl/plan')`。 + +### 6.2 H5 / API(能力) + +运输计划接口继续校验 **Web 菜单码** `pl:plan:*`(及现有按钮权限),不另写一套后端。 + +因此:能在小程序里办计划的角色,必须同时拥有: + +- 小程序入口 `mini:pl:plan:manage` +- 与桌面相同的 `pl:plan:list` 等(否则 H5 调接口 403) + +无 Web 计划权限时:H5 展示「无权限」空态,不跳桌面 403 页。 + +## 7. 运输计划移动端(首发业务) + +视觉与结构对齐 Demo:`bak/mobile-ui/shanxia-mp-pl-plan-webview-demo.html`。规范:`docs/miniprogram-ui-spec-sanqiang.md`。 + +| 能力 | 移动端形态 | 逻辑来源 | +|------|------------|----------| +| 列表 | 日期快捷 + 状态/单据 Tab + 检索 + 卡片 | `planApi` 列表筛选(`PL_PLAN_LIST_TABS`) | +| 查看 | 全屏详情 | 现有查看字段/状态 Tag | +| 新增/编辑 | 全屏分区表单(非桌面弹窗) | `PlPlanFormDialog` 校验与提交,拆成移动分区 | +| 安排/调整运输 | 全屏安排页 | `usePlPlanArrangeActions` 等 | +| 复制 | 详情/列表操作 + 确认 | 现有复制 API | +| 等客户通知 | 同上 | 现有 wait-notice | +| 作废 | 危险操作确认 | 现有 void | +| 打印单据 | 预览页(入库单/送提货单/分装单/空白单) | 现有 print API;无系统打印则引导长按存图 | +| 三方人车快速创建 | 独立全屏页 | 现有 paste-create 逻辑 | + +列表卡片主信息:计划编号、单据类型、计划日期、跟进状态 Tag、车牌、人员摘要、产品/供需摘要。点卡片进详情。 + +**代码复用原则**:抽 composable / 纯函数,避免复制 `index.vue` 整页。桌面弹窗组件可按「移动全屏包装」复用表单子块,不在 WebView 里渲染 `el-dialog` 作为主路径。 + +建议路由(均在 `/#/m` 下): + +```text +/m/pl/plan 列表 +/m/pl/plan/new 新增 +/m/pl/plan/:id 详情 +/m/pl/plan/:id/edit 编辑 +/m/pl/plan/:id/arrange 安排运输 +/m/pl/plan/paste 三方人车 +/m/pl/plan/:id/print 打印预览 +``` + +## 8. 后续模块接入清单(模板) + +下一个模块只需: + +1. `web-admin`:`views/m//...` + 路由 `meta.isMpWebview` +2. 套 `MpWebviewShell` +3. 小程序工作台增加一项:同一 `mp-webview` 壳 + `path=/m/...` +4. 角色同时具备对应 **mini 入口 perms** 与 **Web 业务 perms** +5. 微信业务域名已配则 **不必提审**(仅改 H5) + +禁止:每个模块再做一个 `web-view` 页面、再做一套登录注入。 + +## 9. 错误处理 + +| 情况 | 行为 | +|------|------| +| 未登录打开壳 | 小程序拦截,先去登录页 | +| ticket 过期/已用 | H5 提示后 `reLaunch` 登录或重新进入工作台再开页 | +| API 401 | 清 H5 token,通知小程序登录 | +| API 403 | 页内无权限 | +| 网络失败 | Toast,可重试 | +| 非小程序打开 `/#/m/*` | 无 ticket 则提示「请从山峡化工小程序打开」,不提供 Web 登录入口(防把移动页当第二套后台) | + +## 10. 测试要点 + +- ticket 60s 过期;第二次 exchange 失败 +- 换票后列表能拉到当前租户计划,数据范围与桌面同一账号一致 +- 全量操作在窄屏可走通(至少:新增草稿、安排、复制、等通知、作废、打开打印预览、三方人车) +- 401 回到小程序登录,不到 `/#/auth/login` +- 只发 `web-admin` 静态资源、不发包:卡片文案或样式变更在小程序内可见 +- 现场「运输计划单」原生页回归不受影响 +- 生产 API 加密开启时,exchange 明文、后续业务请求加密均正常 + +## 11. 实现落点(供计划拆任务,非本次编码) + +| 层 | 路径(预期) | +|----|----------------| +| 后端 | `backend/app/api/public.py` 增加 ticket/exchange;Redis TTL | +| H5 壳 | `web-admin/src/layouts/MpWebviewShell.vue`、`src/views/m/**`、守卫分支 | +| 小程序壳 | `miniprogram/pages/common/mp-webview.vue`、`pages.json`、`config/api.js`、`workbench-menus.js` | +| 菜单 SQL | `docs/sql/migrations/2026-09-14-mini-pl-plan-webview-menus.sql` | +| Demo | 已有 `bak/mobile-ui/shanxia-mp-pl-plan-webview-demo.html` | + +## 12. 风险 + +- 运输计划桌面表单极重,移动全屏分区工作量大;模板先行(壳 + ticket + 列表 + 详情),表单可按同一变更内分批提交,但 **范围仍是全量能力**,不把打印/三方人车划出本期。 +- 微信 `web-view` 同一页面只能有一个组件、分享/原生导航受限;打印与文件下载体验弱于桌面。 +- 本地 `WEB_ORIGIN` 不在业务域名内,真机必须指向已配置的测试/生产门户。