三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

SpringBoot+Flowable 移动审批实战:UniApp 待办详情如何把业务单据、viewType 与底栏按钮嵌进同一页

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)}

一次返回通常包含:

  1. processInstance:名称、发起人、状态、businessKey
  2. processDefinition:表单类型(NORMAL / CUSTOM)、定义信息
  3. activityNodes:审批进度时间线
  4. formFieldsPermission:字段可写 / 只读 / 隐藏
  5. 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// 发起人待办落回开始节点时的兜底}}

要点:

  1. 普通审批操作组件与「撤回 / 重提 / 取回」互斥渲染,避免底栏同时出现两套语义冲突的按钮。
  2. from参数支持从业务列表(如用印列表)进详情,办完后回业务页而不是死跳审批 Tab——小程序里裸switchTab容易闪首页,项目里统一用navigateToBpmWorkbench
  3. 表单是否可编辑: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 微信小程序不支持任意动态组件。移动端约定:

  1. bpm-menu-config(或等价菜单/模型配置)声明mobileViewComponent标识
  2. form-detail.vue静态 importv-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


六、审批进度:手机上的「信任来源」

业务同事在手机上最常问的不是「按钮在哪」,而是「现在卡在谁」。详情页中部ProcessInstanceTimelineactivityNodes

▲ 时间线可见多次「发起人撤回」意见与再次发起,移动端也能讲清流程故事

轨迹与底栏解耦:即使当前是只读抄送,进度区仍然完整——知情不办理。评论区可后续扩展,但不阻塞办理闭环。


七、和 PC 详情的差异清单(落地对照)

维度PC(Vben)UniApp
业务组件加载动态registerComponent静态v-if映射
布局左表单右图/轨迹单列卡片堆叠
入口参数路由 query 类似强制带viewType
底栏横向工具条固定 footer + 更多
设计器BPMN / SIMPLE不做重设计,只办理
API/admin-api/bpm/**同源

移动端砍掉的是设计器,不是流程能力——加签、退回、抄送、超时策略仍由同一 Flowable 定义驱动。


八、端到端体验建议

  1. 打开演示环境移动端 / H5,进入「审批」Tab,看四入口。
  2. 用有待办的账号打开一条viewType=todo,确认底栏来自buttonsSetting
  3. 用发起人打开「我的」里审批中单据,应出现撤回而不是通过。
  4. 撤回或驳回后回到同一详情,表单可编辑,底栏变「提交」。
  5. (开发)新增一张 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 支持一下!

← 返回列表