2026-09-14-mp-webview-transport-plan-design.md 12 KB

小程序 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. 架构(模板)

小程序工作台
  → 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/<module>
  → 只需新增 web-admin 页面,不必再提审小程序
┌─────────────┐     ticket      ┌──────────────────┐     JWT      ┌────────────┐
│  miniprogram│ ──────────────► │ web-admin /#/m/* │ ───────────► │  FastAPI   │
│  web-view壳 │ ◄────────────── │ MpWebviewShell   │ ◄─────────── │  既有计划API │
└─────────────┘   401→reLaunch  └──────────────────┘   租户/权限  └────────────┘

边界

单元 职责 依赖
pages/common/mp-webview 取票、拼 URL、承载 <web-view>、接收 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:

https://w.shanxiachem.com/#/m/pl/plan?mpTicket=<ticket>

实现时 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 下):

/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/<module>/... + 路由 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 不在业务域名内,真机必须指向已配置的测试/生产门户。