你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流

一、审批流做到一半,产品经理说"要像钉钉那样"

钉钉的审批界面人人都见过:点个"+"加一级审批,拉个条件分支,再把单子抄送给谁------不用看文档,会用手机就会配。

轮到给自己的系统做审批流,路通常只有两条,都不舒服:

  • 上画布类流程设计器(LogicFlow、BPMN 那种):功能是强,但对普通用户太"工程师"了------拖节点、连线、调坐标,产品经理看完演示说:用户不会用;
  • 硬编码:审批链写死在代码里,加一个审批节点就是一次发版。

还有第三条路:把钉钉那套交互做成开源 Vue3 组件,谁都能装。这不是设想------mldong-flow-designer-dingtalk,npm 装包即用。本篇不讲原理,直接实录:从 npm install 到画出第一条审批流,再到把它喂给一个开源工作流引擎真跑一遍。

二、为什么钉钉不用流程图画布

先想一个问题:审批流的真实形态是什么?

九成场景,它就是"一条主干 + 偶尔的分支 + 少量会签抄送"。审批人头脑里没有"节点和边"的概念,只有三个朴素的疑问:谁先审、谁后审、什么情况走另一条路。

画布把流程建模工具直接交给了用户;钉钉的思路是把流程折叠成一棵:主干一条线走到底,分支收进"条件分支"容器里,连线、坐标这些概念全部消失。所以钉钉的审批设计器,业务人员零学习成本。

我把这套交互实现成了 Vue3 组件,发在 npm 上,两个包:

  • mldong-flow-designer-dingtalk (钉钉精简包):只有钉钉树形设计器。dependencies 为空------没有 LogicFlow、没有 antd/element,peerDependencies 只要求 vue >= 3.4
  • mldong-flow-designer-plus (双模式包):mode 属性一键切换 canvas 画布 / 钉钉树形两种模式,两套视图吃同一份 JSON。

不是 demo 玩具:mldong 系框架的前端(vben5-wf)生产环境在用,开源工作流引擎 jeeflow 的官方前端 jeeflow-ui,流程设计页用的也是它。

三、10 分钟实录:装包到画出第一条审批流

第 1 步:起一个 Vue3 工程,装包

shell 复制代码
npm create vite@latest my-flow -- --template vue
cd my-flow
npm install mldong-flow-designer-dingtalk

我的真机(Windows + Node 22):整个工程连同 Vite 一起装完 32 个包,5 秒。设计器自身零依赖,不会往你项目里拖一棵前端 UI 框架的依赖树。

第 2 步:注册组件,三行

ts 复制代码
import { createApp } from 'vue'
import FlowDesigner from 'mldong-flow-designer-dingtalk'
import 'mldong-flow-designer-dingtalk/lib/style.css'
import App from './App.vue'

createApp(App).use(FlowDesigner).mount('#app')

第 3 步:喂一份 JSON

设计器是个受控组件,v-model:value 进、@on-save 出,都是同一份 JSON。我先喂一条报销审批流:

vue 复制代码
<script setup>
import { ref } from 'vue'
import FlowDesigner from 'mldong-flow-designer-dingtalk'

const graphData = ref({
  name: 'expense',
  displayName: '报销审批',
  type: 'approval',
  nodes: [
    { id: 'start', type: 'snaker:start', x: 100, y: 200, properties: { width: 50, height: 50 }, text: { value: '开始' } },
    { id: 'apply', type: 'snaker:task', x: 200, y: 200, properties: { form: 'apply-form', assignee: 'applicant', taskType: 0, performType: 0 }, text: { value: '发起申请' } },
    { id: 'leader', type: 'snaker:task', x: 320, y: 200, properties: { form: 'expense-form', assignee: 'leader', taskType: 0, performType: 0 }, text: { value: '部门经理审批' } },
    { id: 'decision1', type: 'snaker:decision', x: 450, y: 200, properties: { width: 80, height: 50 }, text: { value: '金额>1000?' } },
    { id: 'manager', type: 'snaker:task', x: 580, y: 120, properties: { form: 'expense-form', assignee: 'manager' }, text: { value: '总经理审批' } },
    { id: 'end', type: 'snaker:end', x: 700, y: 200, properties: { width: 50, height: 50 }, text: { value: '结束' } }
  ],
  edges: [
    { id: 'e0', sourceNodeId: 'start', targetNodeId: 'apply', properties: {} },
    { id: 'e1', sourceNodeId: 'apply', targetNodeId: 'leader', properties: {} },
    { id: 'e2', sourceNodeId: 'leader', targetNodeId: 'decision1', properties: {} },
    { id: 'e3', sourceNodeId: 'decision1', targetNodeId: 'manager', properties: { expr: 'amount > 1000' }, text: { value: '金额>1000' } },
    { id: 'e4', sourceNodeId: 'decision1', targetNodeId: 'end', properties: { expr: 'amount <= 1000' }, text: { value: '否则' } },
    { id: 'e5', sourceNodeId: 'manager', targetNodeId: 'end', properties: {} }
  ]
})

function onSave(data) {
  // 保存:data 就是这份 JSON(外加一个 mode 标记),POST 给后端即可
}
</script>

<template>
  <FlowDesigner v-model:value="graphData" mode="dingtalk" @on-save="onSave" />
</template>

打开页面,钉钉同款界面就出来了------绿色"开始"胶囊、带"✓ 审批人"标签的节点卡、条件分支双列容器:

注意这份 JSON 里的 xy 不是给树形视图用的------它们是画布坐标,钉钉视图只是同一份数据的另一种呈现(组件内部在图结构和树结构之间做双向转换)。存储格式始终是图 JSON,这是后面"直接喂引擎"的伏笔。

加节点:点"+",不拖拽

树形设计器里没有拖拽。节点之间的"+"点开,是钉钉同款的节点选择器:审批人、自定义节点、子流程、条件分支、并行分支:

配节点:审批人怎么定、会不会签

点任意节点卡,右侧弹出属性抽屉。表单绑定、参与人、候选用户/候选用户组、任务类型、会签类型、会签完成条件、操作按钮(同意/拒绝/退回)都在这里配:

顺带一提:canvas 画布模式

如果你的系统还要兼容传统"自由画布"流程,换成双模式包 mldong-flow-designer-plusmode="canvas"mode="dingtalk" 一字切换,两套视图消费同一份 JSON。钉钉模式的对外 API(FDDesignerAPI)是刻意按 LogicFlow 实例的命名对齐实现的------updateTextsetPropertiesgetProperties 这些方法两边都有,二次开发代码基本零修改就能双模式复用。

不想装包?官方在线演示站可以直接玩:flow-designer.mldong.com,首页"案例3: 钉钉模式"还带一个"模拟审批"按钮,能看到单子沿树一级级走过去的高亮效果。

四、画完的图去哪了:一份 JSON,喂给开源引擎直接跑

到这里,设计器部分说完了。但真正有意思的是下一件事:

设计器吐出的这份 JSON,就是一个工作流引擎能直接执行的流程定义。 snaker:start/task/decision/end 节点、边上挂 expr 条件表达式------这不是我为了配合设计器发明的格式,它就是开源工作流引擎 jeeflow 吃的定义格式。设计器相当于这个格式的官方可视化编辑器。

有三个细节值得说:

  1. @on-save 的产物多了个 mode: "dingtalk" 字段,不用清洗 。引擎侧反序列化时未知字段直接忽略(Java 侧 Jackson 关掉了 FAIL_ON_UNKNOWN_PROPERTIES,Go 侧 struct 只映射已知字段),多一个 key 引擎连看都不看;
  2. jeeflow 官方前端 jeeflow-ui 就是这么接的 :设计器 @on-saveupdateDefine 存设计稿 → deploy 生成流程定义(版本号 +1)→ 业务侧 startAndExecute 发起。三步之后,这张图就是一条活流程;
  3. 同一张图,八门语言引擎都能跑。Java/Go/Python/Node/PHP/Rust/MoonBit/C# 的引擎实现消费同一份流程定义------这也是 jeeflow 这个项目的核心玩法。

光说不练假把式,我把这条链路在 jeeflow 的开源演示站(jeeflow-demo.mldong.com,免登录,右上角可切八门语言后端,本节用的是 Go 引擎)真机走了一遍。

第一步,导入设计稿。 演示站的"流程设计"页支持直接导入 JSON------就是第三步那份。导入后设计器把它渲染成钉钉树:

第二步,保存并发布。 后端生成流程定义,版本 +1,这张图从"一张图"变成了"一条可执行流程"。

第三步,发起。 以张三的身份发起(金额 1500),接口信封:

json 复制代码
{ "code": 0, "msg": "成功", "data": { "processInstanceId": "102" } }

申请节点自动由发起人办结(jeeflow 的 applicant 约定),单子落到部门经理李四的待办里。李四打开办理抽屉------流程图上,已办节点绿色打勾,当前节点橙色高亮,走到哪一目了然:

第四步,条件分支真实路由。 金额 1500,命中"金额>1000"分支,单子继续走到总经理王五;王五同意后流程到达结束节点。张三在"我发起的"里看到:已完成,流程图全路径绿色:

设计器画的图、引擎跑的流程、前端回显的高亮,自始至终是同一份 JSON。

一个真实的坑:条件分支不动了

诚实起见,第一次跑这条链路时我翻车了。

第一版我把分支条件写成了 days > 3(按请假天数走分支)。发起、审批都正常,但单子在部门经理审批之后就停住了------不报错,也不往下走。

排查结论:jeeflow 引擎核心是零依赖设计,表达式求值是一个 SPIIExpressionEvaluator),引擎本身不内置任何表达式引擎------你把 Aviator、SpEL、expr-lang 还是自己写的求值器注入进来,引擎就用谁。演示站为了演示,内置了一个极简的桩求值器,只认识 amount 的几种比较;days > 3 它不认识,按约定返回 false。而引擎对决策节点的语义很克制:逐条试边,第一条成立的走;一条都不成立,就地等待,绝不乱走。所以"卡住"不是 bug,是引擎在诚实地说"没有一条路能走"。

换回求值器认识的 amount 条件,全链路立刻走通。这个坑反而让我更喜欢这个设计:表达式引擎的选择权在你手里,代价是你得知道"表达式是 SPI 注入的,不是引擎自带的"。顺带一个细节:从 jeeflow-ui 发起申请时,表单变量会统一加 f_ 前缀(比如 f_amount)------表达式里用哪个变量名,取决于你给引擎喂了什么,这也是接引擎时最容易想当然的地方。

五、什么时候别用:树形设计器的边界

树形设计器不是银弹,它的舒适区是 OA 审批语义:串级审批、条件分支、会签、并行、抄送。超出这个形态的需求,别硬上:

场景 建议
审批流 / 业务单据流转(绝大多数后台系统) 钉钉精简包,就是为这个生的
网状流程、深层子流程嵌套、自由布局诉求 双模式包的 canvas 画布模式
存量画布流程要兼容,又要上钉钉模式 双模式包,mode 切换,同一份 JSON

两个包自 3.1.0 起同版本联动发布;3.1.0 这个版本做了三件事:砍掉 ant-design-vue / element-plus 依赖(内置 UI 全部自研)、补齐工作流属性(流程级 +8:字段权限/抄送人/发起时选人等,任务级 +6:候选用户/会签类型/会签完成条件等)、钉钉模式视觉优化。

如果你的系统正打算做审批流,希望这条 10 分钟的路能帮你省掉一次"自研设计器"的排期。有兴趣聊工作流引擎的,看看文末的 jeeflow 系列。

参考资料

相关推荐
2分钟速写快排2 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60612 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角3 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!3 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰4 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi5 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen6 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马6 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒7 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端