一个 npm 包,九页审批中心:Vue3 项目接上工作流引擎

一、先说结果:61 行装配代码,换来九页能用的流程中心

我在本机开了一个空白 Vite + Vue3 工程,只装两个包,写了两个文件,跑出来是这样:

  • 九页:工作台、发起申请、待办审批、已办、我的申请、抄送我的、流程定义、流程设计、委托代理
  • 三个抽屉:发起、办理、实例详情(详情里带流程图和审批记录两个 tab)
  • 真数据 :工作台那八枚数字卡、近 7 天趋势、流程 Top 5、状态分布,全是后端 stats 三个接口回来的,不是占位骨架
  • 全程没写一行 CSS------包里带 style.css,宿主 import 一次就完事

装配代码一共 61 行(main.js 29 行 + App.vue 32 行),加上两张业务表单是 112 行。这篇讲三件事:账是多少 、表单这条腿为什么包不能替你写完 、一个 baseUrl 换八套后端会发生什么。

二、把账摆出来:装包、体积、导出面

装包。 干净工程里:

bash 复制代码
npm i vue            # 23 个包
npm i @mldong/jeeflow-ui   # 再加 2 个包

那 2 个包是组件包本体,和它的一个 peer(钉钉风格设计器 mldong-flow-designer-dingtalk,流程设计页要用)。包自己的 dependencies 是空的 ------npm ls 里它对 vue 和设计器都显示 deduped,也就是全部骑在宿主已有的那份上,没有第二份 vue。

体积。 发布物解包 584,523 字节(约 571 KB),tarball 133,409 字节。真正进浏览器的是两个文件:

文件 字节
dist/jeeflow-ui.js(ESM) 272,094
dist/style.css 54,993

导出面。 从发布物的 dist/index.d.ts 数出来的组件导出是 21 个,分四类:

  • 整页 9 个:工作台、发起申请、待办审批、已办、我的申请、抄送我的、流程定义、流程设计、委托代理
  • 抽屉 3 个:发起、办理、实例详情
  • 布局 1 个 :JfLayout(顶部 + 左侧菜单 + 内容区)
  • 基础件 8 个:抽屉壳、徽标、流程图查看器、选人器、图标、tab、审批记录、发起附加区

它替你吃掉了多少接口。 包里 api.ts 一共调用统一门面的 42 个 action ------processDefine/* 8 个、processInstance/* 14 个(含 stats 三个)、processTask/* 10 个、processDesign/* 8 个、processSurrogate/* 3 个(有个 action 跨两组)。这就是"九页能直接用"的全部来源:接口形状、分页参数、错误码判断都在包里,宿主的 REST 层一行不用写。

三、装配就三块:注入配置、给菜单、注册表单

main.js------把引擎地址和"我是谁"注入进去:

js 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import '@mldong/jeeflow-ui/dist/style.css'
import { createJeeflowUi, JeeflowUiKey }
  from '@mldong/jeeflow-ui'
import ApplyForm from './forms/apply-form.vue'
import LeaveForm from './forms/leave-form.vue'

const jeeflowUi = createJeeflowUi({
  // 传函数:每次请求现取,切后端不 reload 也不重建实例
  baseUrl: () => localStorage.getItem('jf_base') || '/java-api',
  getOperator: () => localStorage.getItem('op') || 'user1',
  getToken: () => null,          // 有登录态则发 Bearer
  hasPermission: () => true,     // 按 wf:{action} 权限码控显隐
  adapters: {                    // 选人/角色/字典/上传,缺则降级
    listUsers: async (kw) =>
      USERS.filter((u) => !kw || u.realName.includes(kw)),
    getUsersByIds: async (ids) =>
      USERS.filter((u) => ids.includes(u.userId)),
  },
})
jeeflowUi.registerForm('apply-form', ApplyForm)
jeeflowUi.registerForm('leave-form', LeaveForm,
  { scenes: ['approve'] })

createApp(App).provide(JeeflowUiKey, jeeflowUi).mount('#app')

App.vue------菜单项里给 key/title/icon,内容区渲染对应页面:

vue 复制代码
<template>
  <JfLayout :menus="menus" title="我的系统 · 流程中心"
            :default-key="key" @select="(k) => (key = k)">
    <component :is="pages[key]" :key="key" />
  </JfLayout>
</template>

<script setup>
import { ref } from 'vue'
import { JfLayout, JfWorkbenchPage, JfApplyListPage,
  /* 余七页同名规则略 */ } from '@mldong/jeeflow-ui'

const pages = {
  workbench: JfWorkbenchPage, apply: JfApplyListPage,
}
const menus = [
  { key: 'workbench', title: '工作台', icon: 'home' },
  { key: 'apply', title: '发起申请', icon: 'apply' },
  /* 余七项略 */
]
const key = ref('workbench')
</script>

两个容易漏的点,都是我这次真撞上的:

  1. 样式要手工 import 。import '@mldong/jeeflow-ui/dist/style.css' 这一行不能省------ESM 产物里没有内联样式的副作用导入,漏了就是九页裸奔(没背景、没间距、表格糊在一起)。
  2. baseUrl 建议传函数而不是字符串。传字符串时地址被固定在创建那一刻;传函数则每次请求现取,切后端、切租户只改 localStorage,页面不 reload。

四、表单这条腿,包真的不替你写

审批界面里唯一没法通用的就是业务表单------请假单和报销单长得就不一样。包的边界是:表单组件由宿主注册,formKey 是从流程定义里读出来的。

我故意先什么都不注册,看看会怎样。办理抽屉打开,"申请信息"下面是这么一句话:

css 复制代码
未注册办理表单「leave-form」,宿主 registerForm 后即可填写

发起抽屉则是另一档:

复制代码
未配置表单字段,可直接发起

没有整段静默消失。这很重要------表单没回显时,"数据没传回来"和"宿主没注册组件"是两种完全不同的排查方向,可读提示直接把后者点名了。

补上注册之后(第三节那两行 registerForm),办理抽屉变成这样:上面是只读的"申请信息"(请假事由、请假天数),中间是我自己写的"核准天数"输入框,下面是审批意见和六个动作按钮(同意/拒绝/退回上一步/退回发起人/跳转/转办)。

表单组件的契约就三条:

  • props 收 modelValue(一个普通对象)+ view(true 为只读明细)
  • 字段名带前缀:申请级 f_、办理级 tf_------引擎按这个前缀把数据往返到实例变量和任务表单数据
  • 建议 defineExpose({ validate }),返回错误文案或 null,包会在提交前调用

一个 5 字段、发起/详情两态的表单,我写了 33 行;办理侧只填一个字段,18 行。这部分成本谁也省不掉,任何一套审批前端都一样,只是包不假装能替你写。

五、闭环真跑了一遍:同一张单从发起到办结

拿同一张请假单走完整条链,每一步都留了读数:

  1. 发起 :张三(user1)在"发起申请"点卡片、填事由和天数、点发起 ------ 提示「发起成功」,新实例 id 94012933769322,state=10
  2. 定位 :切到李四(leader),在"待办审批"里找到这张单,点办理 ------ 抽屉里"申请信息"回显的事由与发起时逐字一致
  3. 办理:填核准天数 2.5、写审批意见,点同意 ------ 提示「办理成功」
  4. 复核 :回查同一个实例 ------ state=20(已办结),审批记录 2 条,两个节点时间都是 12:51
  5. 看图:详情抽屉切"流程图"tab ------ 四节点全绿(已完成),处理人显示姓名不是 id

第 5 步那张图值得单看:流程图是包里的 JfFlowViewer 画的,它吃的是 processInstance/highLight 的返回------节点上那些绿框、勾、姓名,都是接口回来现画的,宿主一行绘图代码没写。

六、一个 baseUrl 换八套后端:六通、两台被 CORS 拦

因为数据层只依赖统一门面(POST {baseUrl}/wf/{action}),我把 baseUrl 换成演示站八种语言的后端,前端代码一个字没改:

后端 跨域直连 九页渲染 待办行数
Java 通 正常 6
Go 通 正常 6
Node.js 通 正常 6
Python 通 正常 6
PHP 通 正常 5
C# 通 正常 6
Rust 不通 空态 0
MoonBit 不通 空态 0

rust 和 moon 那两格红得很具体:预检请求 OPTIONS 一个回 405、一个回 204 但没带 Access-Control-Allow-Origin,浏览器直接把后续 fetch 拦掉。这是演示站网关配置的差异,不是引擎或组件的问题------自己部署时前端和后端同域(vite proxy 或 nginx 反代),这两台一样能用。我把它记下来是因为它会误导第一次试的人:页面全空、控制台一片红,看着像包坏了。

六套通的后端之间也有出口形状的差异,页面都容忍了:Go 和 Rust 的信封没有 msg 字段,Go 的 createTime 是 0001-01-01... 这种零值形状,页面照样把列表排出来。

七、哪些页开箱即用、哪些还要后端配合

九页不是同一档,说清楚:

  • 八页开箱即用:工作台、发起申请、待办、已办、我的申请、抄送我的、流程定义、委托代理------只要后端实现了门面这些 action。
  • 流程设计页要扩展仓储 。它读的是"设计稿"(processDesign/*,落在 wf_process_design 表),演示站的 Python 后端只预置了已发布定义、没注册扩展仓储,所以这页是空态「暂无流程设计」,包里的注释也写了这一档:后端未注册扩展仓储时 processDesign/* 不可用。
  • 发起申请页有兜底 。它先问 processDesign/listByType,演示站回的是空对象 {},于是回落到"已发布定义"列表------所以我截图里那 16 张流程卡来自定义表而不是设计稿表。
  • 委托页那一格显示的是 id 。我的委托列表里"被委托人"是 manager 这个字符串,这一页没做 id→姓名 的解析(其他页做了)。

八、什么时候用它,什么时候别用

  • 该用它:Vue3 工程 + 任意语言后端,要快速有整套审批界面 ------ 九页加三抽屉直接拿
  • 该用它:后端只实现了门面那 40 多个 action ------ 这是它唯一的接口依赖
  • 谨慎:已有自己的设计语言(antd / element 深度定制)------ 它自带一套朴素样式、不依赖任何 UI 库,但改视觉得覆盖 CSS 变量
  • 只取零件:只要流程图或审批记录那几块 ------ 可以只引查看器和记录组件,不必上整套九页
  • 换路子 :要把流程中心嵌进非 Vue 系统(若依、老 JSP)------ 走 iframe 壳那条路(仓库里的 apps/embed),别在老页面里引 Vue
  • 不适用:需要移动端 ------ 移动端是另一件东西(uni-app x 的审批端),和这个包共用同一套门面

包和仓库都是 Apache-2.0,npm 装得到,GitHub 也开着。

参考资料(发布版保留纯文本链接)

相关推荐
高级程序源9 小时前
django校园寻物通失物招领平台32261-计算机课程设计、毕业设计
vue.js·python·mysql·小程序·django·flask·课程设计
WeiXin_DZbishe9 小时前
基于springboot宠物领养系统-计算机毕设 附源码【课程设计】94217
vue.js·spring boot·python·django·php·课程设计
架构技术专栏9 小时前
Agent 工具调用:从工具声明到执行反馈
前端·人工智能·后端
别动我齐刘海10 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
志尊宝11 小时前
Vue3 零基础每日笔记(071):暗黑模式与主题切换——CSS 变量 + Element Plus 暗黑方案
前端·css·笔记·vue·html·前端开发·软件开发
WeiXin_DZbishe11 小时前
基于springboot城市公交实时运营与调度系统-计算机毕设 附源码【课程设计】66173
java·vue.js·spring boot·后端·python·php·课程设计
刘发财11 小时前
前端如何生成10000页PDF文件?
前端·javascript·开源
WeiXin_DZbishe12 小时前
基于Springboot+AI的kuall音乐广场的设计与实现-计算机毕设 附源码【课程设计】12715
vue.js·人工智能·spring boot·后端·python·php·课程设计
A接拉起00712 小时前
2026 年 JavaScript 开发者需要了解的知识
前端·javascript·后端