SpringBoot+Flowable 移动审批实战:UniApp 待办详情如何把业务单据、viewType 与底栏按钮嵌进同一页
SpringBoot+Flowable 移动审批实战:UniApp 待办详情如何把业务单据、viewType 与底栏按钮嵌进同一页
🌐演示地址:http://ruoyioffice.com | 📦源码1·GitHub:ruoyi-office | 📦源码2·GitCode:ruoyi-office | 📦源码3·Gitee:ruoyi-office | 💬微信:17156169080(备注「RuoYi Office」)
PC 上审批详情可以左右分栏、动态挂业务组件;手机屏幕只有一列,却要同时装下「流程头、业务单、进度、通过/拒绝/更多」。很多人一上来就拆成十几个页面,结果状态一乱、按钮错位、小程序包体积爆炸。本文讲 RuoYi Office 怎么用Spring Boot + Flowable 同一套
/admin-api/bpm,在 UniApp 里把移动审批收成一个详情页 + 一个 viewType 门禁。
▲ 全景:上层 UniApp 四 Tab 工作台 → 中层同一详情页(头/单/轨迹/底栏)→ 底层 Spring Boot + Flowable
引言:移动审批真正难的是「同一页多身份」
同一条流程实例,可能被四种身份打开:
| viewType | 入口 | 用户心智 | 底栏期望 |
|---|---|---|---|
todo | 待办 | 我要办 | 通过 / 拒绝 / 转办 / 退回… |
my | 我的 | 我发起的 | 审批中可撤回;驳回/撤回后可重提 |
done | 已办 | 我审过的 | 条件满足时可取回 |
copy | 抄送 | 知会我的 | 通常只读,无办理按钮 |
如果为每种身份各做一套页面,业务表单会复制四遍;如果完全不管来源,又会出现「抄送人看到通过按钮」「已办里误点拒绝」。正确做法是:路由只带id/taskId/viewType,详情页按门禁切换底栏,中间业务区与轨迹区复用。
一、三端同源:先别在 App 里直连 Flowable
移动端、PC 管理端、H5 全部走:
GET /admin-api/bpm/process-instance/get-approval-detail ?processInstanceId=... &taskId=...UniApp API 层封装:
exportfunctiongetApprovalDetail(params:{processDefinitionId?:stringprocessInstanceId?:stringactivityId?:stringtaskId?:stringprocessVariablesStr?:string}){returnhttp.get<ApprovalDetail>('/bpm/process-instance/get-approval-detail',params)}一次返回通常包含:
processInstance:名称、发起人、状态、businessKeyprocessDefinition:表单类型(NORMAL / CUSTOM)、定义信息activityNodes:审批进度时间线formFieldsPermission:字段可写 / 只读 / 隐藏todoTask:当前待办(含buttonsSetting)
App 不解析 BPMN XML,不自己算候选人——权限、租户、按钮开关都在服务端收敛,和 PC 待办详情对齐。
▲ 移动审批工作台:待办 / 我的 / 已办 / 抄送,点进详情时把 viewType 带上
二、详情页骨架:头 + 单 + 轨迹 + 底栏
页面路径约定:
/pages-bpm/processInstance/detail/index ?id={processInstanceId} &taskId={taskId?} &viewType=todo|my|done|copy &from={可选业务列表回跳}结构对应四个区块:
┌─────────────────────────────┐ │ Navbar:审批详情 │ ├─────────────────────────────┤ │ 流程头:标题 / 发起人 / 时间 │ │ + 状态印章(盖章) │ ├─────────────────────────────┤ │ FormDetail:NORMAL 或 CUSTOM│ ├─────────────────────────────┤ │ 审批进度 Timeline │ ├─────────────────────────────┤ │ 底栏:操作按钮 / 撤回 / 提交 │ └─────────────────────────────┘流程头用状态枚举映射静态 SVG 印章(审批中 / 通过 / 不通过 / 已撤回…),手机上一眼能读出「这单现在什么态」,比纯文字 Tag 更适合拇指浏览。
▲ 实机:OA 通用报销申请 · 已撤回印章 · 表单可编辑 · 底栏「提交」——同一详情页按状态切换动作
三、viewType 门禁:底栏三选一,而不是按钮大杂烩
加载详情后的核心分支(逻辑摘要):
constvt=props.viewType||'todo'conststatus=data.processInstance.statusconstisResubmitStatus=status===REJECT||status===WITHDRAW||status===NOT_STARTif(isResubmitStatus&&(vt==='my'||vt==='todo')){showResubmit=true// 底栏:提交}elseif(vt==='my'&&status===RUNNING){showRevoke=true// 底栏:撤回}elseif(vt==='done'&&taskId){loadWithdrawStatus()// 可能显示:取回}elseif(vt==='todo'){if(data.todoTask){operationButton.init(...)// 底栏:通过/拒绝/更多}elseif(status===RUNNING){showRevoke=true// 发起人待办落回开始节点时的兜底}}要点:
- 普通审批操作组件与「撤回 / 重提 / 取回」互斥渲染,避免底栏同时出现两套语义冲突的按钮。
from参数支持从业务列表(如用印列表)进详情,办完后回业务页而不是死跳审批 Tab——小程序里裸switchTab容易闪首页,项目里统一用navigateToBpmWorkbench。- 表单是否可编辑:
showResubmit或字段权限含「可写(2)」时打开。
这套门禁把「审批反悔」三态也接进来了(撤回 / 取回 / 重提),下一篇会专门拆状态机;本文先记住:移动端详情页是状态机的 UI 投影。
四、buttonsSetting:底栏不是写死「通过+拒绝」
待办场景下,operation-button.vue读取todoTask.buttonsSetting:
- 右侧最多两个主按钮:通常拒绝(plain danger)+ 通过(primary)
- 左侧最多两个图标操作:转办、委派、退回、加签、抄送…
- 超出进「更多」ActionSheet
Object.keys(task.buttonsSetting||{}).forEach((key)=>{constoperationType=Number(key)if(task.buttonsSetting[key].enable&&isHandleTaskStatus()&&!rightOperationTypes.includes(operationType)){// 左侧满 2 个 → 进更多}})显示名优先用模型配置的displayName(比如把「通过」改成「同意付款」),与 PC 办理区同源。操作页继续拆分子路由(意见填写、选人转办、选节点退回),避免详情页堆满弹层导致小程序 setData 抖动。
五、CUSTOM 业务单:小程序不能动态组件,就静态 v-if
PC Web 可用registerComponent(formCustomViewPath)动态挂业务详情;UniApp 微信小程序不支持任意动态组件。移动端约定:
- 在
bpm-menu-config(或等价菜单/模型配置)声明mobileViewComponent标识 - 在
form-detail.vue静态 import并v-if匹配
<template v-if="resolvedFormType === BpmModelFormType.CUSTOM"> <LeaveDetail v-if="viewComponent === 'LeaveDetail'" ... /> <SealApplyDetail v-else-if="viewComponent === 'SealApplyDetail'" :todo-task="todoTask" :view-type="viewType" ... /> <LeaveCancelDetail v-else-if="viewComponent === 'LeaveCancelDetail'" ... /> <!-- 用车 / 会议室 / 入职转正离职调动 / 合同 … --> </template>新增业务单三步:配标识 → import → 加一条v-else-if。个别单据(用印归还、销假)还要把todoTask/viewType下传,才能在「申请人归还印章」这类节点打开局部可编辑区——这是移动端相对 PC 更「显式」的代价,也是包体积可控的前提。
NORMAL 流程表单则走动态 schema 渲染,字段权限直接吃formFieldsPermission。
六、审批进度:手机上的「信任来源」
业务同事在手机上最常问的不是「按钮在哪」,而是「现在卡在谁」。详情页中部ProcessInstanceTimeline吃activityNodes:
▲ 时间线可见多次「发起人撤回」意见与再次发起,移动端也能讲清流程故事
轨迹与底栏解耦:即使当前是只读抄送,进度区仍然完整——知情不办理。评论区可后续扩展,但不阻塞办理闭环。
七、和 PC 详情的差异清单(落地对照)
| 维度 | PC(Vben) | UniApp |
|---|---|---|
| 业务组件加载 | 动态registerComponent | 静态v-if映射 |
| 布局 | 左表单右图/轨迹 | 单列卡片堆叠 |
| 入口参数 | 路由 query 类似 | 强制带viewType |
| 底栏 | 横向工具条 | 固定 footer + 更多 |
| 设计器 | BPMN / SIMPLE | 不做重设计,只办理 |
| API | /admin-api/bpm/** | 同源 |
移动端砍掉的是设计器,不是流程能力——加签、退回、抄送、超时策略仍由同一 Flowable 定义驱动。
八、端到端体验建议
- 打开演示环境移动端 / H5,进入「审批」Tab,看四入口。
- 用有待办的账号打开一条
viewType=todo,确认底栏来自buttonsSetting。 - 用发起人打开「我的」里审批中单据,应出现撤回而不是通过。
- 撤回或驳回后回到同一详情,表单可编辑,底栏变「提交」。
- (开发)新增一张 CUSTOM 单:只改配置 +
form-detail一条分支,不新开详情壳。
在线演示:http://ruoyioffice.com
源码仓库:GitHub | GitCode | Gitee
常见问题(FAQ)
为什么必须传 viewType?不能只靠后端判断身份吗?
后端仍会校验权限;viewType解决的是UI 语义:同一用户可能既是发起人又是审批人。入口明确后,底栏不会在「撤回」和「通过」之间歧义闪烁。
小程序主包会不会被业务详情撑爆?
业务详情放在pages-bpm分包,跨分包组件按规范?async或上收主包公共件;主包体积用pnpm check:mp-size卡死在 2MB 内。
操作成功后为什么不直接 switchTab?
部分端上switchTab会先闪首页。项目统一navigateToBpmWorkbench,并uni.$emit('bpm-list-refresh')刷新列表。
移动端要不要独立一套审批 API?
不要。独立 API = 双倍权限漏洞面。坚持/admin-api/bpm,Token、租户、数据权限与 PC 一致。
和「多人审批 / 超时 / 抄送」文章什么关系?
那些是引擎与节点能力;本文是手机上的承载壳:同一详情如何嵌单、如何按入口露按钮。
结语
移动审批做成「能点通过」很容易;做成四入口共用一页、业务单可嵌、底栏跟模型配置走、小程序体积可控,才是产品能长期加单据的骨架。RuoYi Office 的选择很克制:Spring Boot + Flowable 出详情聚合接口,UniApp 用 viewType 门禁投影状态,CUSTOM 用静态映射换可预期的包体。
你们团队移动端现在是「只能看不能办」,还是已经卡在「业务单嵌不进详情」?欢迎评论区交流。
💡想要体验 RuoYi Office 的强大功能?
🌐在线演示:http://ruoyioffice.com/web/(账号 admin / admin123)
📦源码仓库:GitHub | GitCode | Gitee
💬技术咨询:添加微信17156169080,备注「RuoYi Office」
⭐如果觉得不错,请给个 Star 支持一下!
