用Vue-super-flow + Element UI快速构建企业级审批流原型
在企业内部管理系统中,审批流程是最常见的功能需求之一。传统的手工绘制流程图方式不仅效率低下,而且难以与业务系统无缝集成。现在,借助Vue-super-flow这一强大的Vue流程图组件,配合Element UI的表单控件,我们可以快速搭建出既美观又实用的审批流程原型。
1. 为什么选择Vue-super-flow构建审批流
审批流程是企业运营中不可或缺的环节,从请假申请到采购审批,几乎每个业务场景都需要流程化的管理。传统方式下,产品经理和前端开发者需要花费大量时间在Visio或Axure中绘制静态流程图,然后再由开发人员手动实现交互逻辑,这种工作模式存在几个明显痛点:
- 效率低下:从设计到实现需要多次转换工具
- 维护困难:业务变更时需同步修改设计和代码
- 交互体验差:静态流程图无法真实反映系统行为
Vue-super-flow作为专为Vue生态设计的流程图组件,完美解决了这些问题。它提供了:
- 可视化拖拽:通过简单拖放即可创建流程节点
- 高度可定制:支持自定义节点样式和连线风格
- 数据驱动:流程状态完全由数据控制
- 无缝集成:与Element UI完美配合,构建完整表单
// 典型审批流节点配置示例 const approvalNodes = [ { id: "submit", type: "start", meta: { name: "提交申请", approver: "" } }, { id: "first_approve", type: "process", meta: { name: "部门审批", approver: "department_manager" } } // 更多节点... ]2. 10分钟快速搭建审批流原型
2.1 环境准备与基础配置
首先确保项目中已安装Vue-super-flow及其依赖:
npm install vue-super-flow element-ui然后在main.js中进行全局注册:
import Vue from 'vue' import ElementUI from 'element-ui' import SuperFlow from 'vue-super-flow' import 'vue-super-flow/lib/index.css' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI) Vue.use(SuperFlow)2.2 构建审批流画布
创建一个基本的审批流编辑器组件:
<template> <div class="approval-flow-editor"> <div class="node-palette"> <div v-for="item in nodeTypes" :key="item.type" class="node-item" draggable @dragstart="dragStart(item)" > {{ item.label }} </div> </div> <div class="flow-container" @drop="dropNode" @dragover.prevent> <super-flow ref="flow" :node-list="nodes" :link-list="links" @connection-created="onConnection" > <template v-slot:node="{ meta }"> <div :class="`flow-node ${meta.type}`"> <div class="node-header"> <span>{{ meta.name }}</span> <i class="el-icon-close" @click="removeNode(meta.id)"></i> </div> <div class="node-body"> <el-select v-if="meta.type === 'process'" v-model="meta.approver" placeholder="选择审批人"> <el-option v-for="user in approvers" :key="user.id" :label="user.name" :value="user.id"> </el-option> </el-select> </div> </div> </template> </super-flow> </div> </div> </template>2.3 审批节点类型设计
典型的审批流程包含几种核心节点类型:
| 节点类型 | 图标 | 功能描述 | 特有属性 |
|---|---|---|---|
| start | ◉ | 流程起点 | 申请人信息 |
| process | ▯ | 审批环节 | 审批人、审批规则 |
| condition | ◇ | 条件分支 | 条件表达式 |
| end | ◉ | 流程终点 | 处理结果 |
// 节点数据示例 data() { return { nodeTypes: [ { type: 'start', label: '开始节点' }, { type: 'process', label: '审批环节' }, { type: 'condition', label: '条件分支' }, { type: 'end', label: '结束节点' } ], approvers: [ { id: 'manager', name: '部门经理' }, { id: 'director', name: '总监' }, { id: 'finance', name: '财务审核' } ] } }3. 实现审批流的业务逻辑
3.1 节点拖拽与连接
Vue-super-flow提供了完整的拖拽API和连接线事件:
methods: { dragStart(item) { event.dataTransfer.setData('nodeType', item.type) }, dropNode(event) { const type = event.dataTransfer.getData('nodeType') const coordinate = this.$refs.flow.getMouseCoordinate( event.clientX, event.clientY ) const newNode = { id: `node_${Date.now()}`, type, coordinate, meta: { name: `${type}节点`, approver: '' } } this.nodes.push(newNode) }, onConnection(connection) { this.links.push({ source: connection.sourceId, target: connection.targetId, meta: { label: '' } }) } }3.2 审批规则配置
每个审批节点都可以配置详细的审批规则:
<el-dialog title="审批节点配置" :visible.sync="showConfig"> <el-form label-width="100px"> <el-form-item label="审批人类型"> <el-radio-group v-model="currentNode.meta.approverType"> <el-radio label="specific">指定人员</el-radio> <el-radio label="role">角色</el-radio> <el-radio label="variable">变量</el-radio> </el-radio-group> </el-form-item> <el-form-item v-if="currentNode.meta.approverType === 'specific'" label="选择审批人"> <el-select v-model="currentNode.meta.approver"> <el-option v-for="user in users" :key="user.id" :label="user.name" :value="user.id"> </el-option> </el-select> </el-form-item> <el-form-item label="审批方式"> <el-checkbox v-model="currentNode.meta.allowDelegate">允许转审</el-checkbox> <el-checkbox v-model="currentNode.meta.allowComment">必须填写意见</el-checkbox> </el-form-item> </el-form> </el-dialog>3.3 条件节点逻辑处理
对于条件分支节点,需要配置判断条件:
// 条件节点配置示例 { id: "condition_1", type: "condition", meta: { name: "金额判断", conditions: [ { expression: "amount <= 5000", target: "approve_1" }, { expression: "amount > 5000", target: "approve_2" } ] } }4. 高级功能与性能优化
4.1 审批流模板管理
将常用审批流程保存为模板:
methods: { saveAsTemplate() { const template = { name: this.templateName, nodes: this.nodes, links: this.links, createdAt: new Date() } // 保存到本地存储或后端 const templates = JSON.parse(localStorage.getItem('flowTemplates') || '[]') templates.push(template) localStorage.setItem('flowTemplates', JSON.stringify(templates)) this.$message.success('模板保存成功') }, loadTemplate(id) { const templates = JSON.parse(localStorage.getItem('flowTemplates') || '[]') const template = templates.find(t => t.id === id) if (template) { this.nodes = template.nodes this.links = template.links } } }4.2 大型流程的性能优化
当审批流程非常复杂时,可以采取以下优化措施:
- 虚拟滚动:只渲染可视区域内的节点
- 分组折叠:将相关节点分组并可折叠
- 懒加载:动态加载流程的不同部分
- 简化渲染:在拖拽过程中使用简化版的节点渲染
// 虚拟滚动配置示例 <super-flow ref="flow" :node-list="visibleNodes" :viewport="viewport" @viewport-change="updateViewport" > <!-- 节点插槽 --> </super-flow> // 计算可见节点 computed: { visibleNodes() { return this.nodes.filter(node => node.coordinate[0] >= this.viewport.x && node.coordinate[0] <= this.viewport.x + this.viewport.width && node.coordinate[1] >= this.viewport.y && node.coordinate[1] <= this.viewport.y + this.viewport.height ) } }4.3 与后端API集成
审批流程通常需要与后端服务深度集成:
// 保存流程到后端 async saveFlow() { try { const flowData = { name: this.flowName, nodes: this.nodes, links: this.links, version: '1.0' } const response = await axios.post('/api/workflow', flowData) this.$message.success('流程保存成功') return response.data.id } catch (error) { this.$message.error('保存失败: ' + error.message) console.error('保存错误:', error) } } // 从后端加载流程 async loadFlow(id) { try { const response = await axios.get(`/api/workflow/${id}`) this.nodes = response.data.nodes this.links = response.data.links this.flowName = response.data.name } catch (error) { this.$message.error('加载失败: ' + error.message) } }5. 实际案例:请假审批流程实现
让我们通过一个完整的请假审批流程示例,展示Vue-super-flow的实际应用。
5.1 流程节点设计
典型的请假审批流程包含以下节点:
- 申请提交:员工填写请假信息
- 部门审批:直属领导审批
- HR备案:人力资源部记录
- 特殊审批:超过3天需要额外审批
- 结果通知:通知申请人结果
const leaveFlow = { nodes: [ { id: 'start', type: 'start', coordinate: [100, 50], meta: { name: '请假申请', form: 'leave_apply' } }, { id: 'dept_approve', type: 'process', coordinate: [100, 200], meta: { name: '部门审批', approver: 'direct_leader', form: 'approval_form' } }, { id: 'hr_record', type: 'process', coordinate: [100, 350], meta: { name: 'HR备案', approver: 'hr_staff', autoApprove: true } }, { id: 'condition', type: 'condition', coordinate: [300, 200], meta: { name: '天数判断', expression: 'days > 3' } }, { id: 'extra_approve', type: 'process', coordinate: [500, 200], meta: { name: '高管审批', approver: 'senior_manager', visible: false } }, { id: 'end', type: 'end', coordinate: [100, 500], meta: { name: '流程结束' } } ], links: [ { source: 'start', target: 'dept_approve' }, { source: 'dept_approve', target: 'condition' }, { source: 'condition', target: 'extra_approve', meta: { label: '是', expression: 'days > 3' } }, { source: 'condition', target: 'hr_record', meta: { label: '否', expression: 'days <= 3' } }, { source: 'extra_approve', target: 'hr_record' }, { source: 'hr_record', target: 'end' } ] }5.2 动态表单集成
每个节点可以关联不同的表单:
<template> <div class="node-form"> <component v-if="currentForm" :is="currentForm.component" v-model="formData" :fields="currentForm.fields" /> </div> </template> <script> import LeaveApplyForm from './forms/LeaveApplyForm.vue' import ApprovalForm from './forms/ApprovalForm.vue' export default { components: { LeaveApplyForm, ApprovalForm }, data() { return { forms: { leave_apply: { component: 'LeaveApplyForm', fields: [ { name: 'type', label: '请假类型', type: 'select', options: [...] }, { name: 'days', label: '请假天数', type: 'number' }, { name: 'reason', label: '请假原因', type: 'textarea' } ] }, approval_form: { component: 'ApprovalForm', fields: [ { name: 'result', label: '审批结果', type: 'radio', options: [...] }, { name: 'comment', label: '审批意见', type: 'textarea' } ] } }, formData: {} } }, computed: { currentForm() { const node = this.selectedNode return node ? this.forms[node.meta.form] : null } } } </script>5.3 流程状态跟踪
实时显示审批流程的当前状态:
<template> <div class="flow-status"> <div v-for="node in nodes" :key="node.id" :class="['status-node', getStatusClass(node)]" > <div class="node-icon"></div> <div class="node-info"> <h4>{{ node.meta.name }}</h4> <p v-if="node.meta.approver"> 审批人: {{ getApproverName(node.meta.approver) }} </p> <p v-if="getNodeStatus(node) === 'completed'"> 完成时间: {{ getCompletionTime(node) }} </p> </div> </div> </div> </template> <script> export default { methods: { getStatusClass(node) { const status = this.getNodeStatus(node) return `status-${status}` }, getNodeStatus(node) { // 根据实际业务逻辑返回节点状态 // pending, processing, completed, rejected return this.flowStatus[node.id] || 'pending' }, getApproverName(approverId) { return this.approvers.find(a => a.id === approverId)?.name || approverId } } } </script> <style> .status-node { border-left: 4px solid #ddd; padding: 10px; margin: 5px 0; transition: all 0.3s; } .status-pending { opacity: 0.6; } .status-processing { border-left-color: #409EFF; background: rgba(64, 158, 255, 0.1); } .status-completed { border-left-color: #67C23A; } .status-rejected { border-left-color: #F56C6C; } </style>6. 最佳实践与常见问题
6.1 审批流设计原则
在设计企业审批流程时,应遵循以下原则:
- 简洁性:避免过度复杂的审批层级
- 灵活性:支持条件分支和动态审批人
- 可追溯性:完整记录审批过程和意见
- 用户体验:清晰的流程状态展示
6.2 常见问题解决方案
问题1:节点过多导致界面混乱
解决方案:
- 使用子流程概念,将相关节点分组
- 实现缩放和平移功能
- 添加搜索和筛选功能
// 子流程实现示例 { id: 'finance_approval', type: 'subflow', meta: { name: '财务审批流程', expanded: false, nodes: [...], links: [...] } }问题2:审批人动态变化
解决方案:
- 支持变量表达式指定审批人
- 提供审批人候选列表
- 允许审批人转审
// 动态审批人配置 { meta: { approver: "${applicant.department.manager}", fallbackApprovers: ["hr_manager", "ceo"] } }问题3:流程版本控制
解决方案:
- 每次修改保存为新版本
- 维护版本变更历史
- 提供流程比较功能
// 版本控制数据结构 { id: "flow_123", name: "采购审批流程", currentVersion: "v2.1", versions: [ { version: "v2.1", createdAt: "2023-05-20", author: "user123", changes: "增加财务审批节点" }, { version: "v2.0", createdAt: "2023-03-15", author: "user456", changes: "优化条件分支逻辑" } ] }6.3 调试技巧
开发过程中可以使用以下方法调试审批流:
- 导出流程图数据:
this.$refs.flow.toJSON()- 验证流程完整性:
function validateFlow(nodes, links) { // 检查是否有孤立节点 // 检查是否有无效连接 // 检查必填属性是否完整 }- 使用模拟数据测试:
const mockData = { applicant: { id: 'user001', department: { manager: 'manager123' } }, formData: { days: 5, reason: '家庭事务' } } function runFlowTest(flow, data) { // 模拟流程执行过程 // 验证每个节点的处理结果 }7. 扩展思路:将审批流引擎化
对于更复杂的企业需求,可以考虑将审批流抽象为引擎:
7.1 流程定义语言
设计一种DSL来描述审批流程:
flow: name: 请假审批 version: 1.0 start: submit nodes: submit: type: start form: leave_apply next: dept_approve dept_approve: type: process approver: ${applicant.manager} actions: approve: next: check_days reject: next: end check_days: type: condition rules: - when: ${form.days > 3} next: extra_approve - default: next: hr_record7.2 流程执行引擎
实现一个可以解析和执行流程定义的引擎:
class WorkflowEngine { constructor(flowDefinition) { this.flow = flowDefinition this.currentState = { node: this.flow.start, data: {}, history: [] } } async execute(action, payload) { const currentNode = this.flow.nodes[this.currentState.node] const transition = currentNode.actions[action] if (!transition) { throw new Error(`Invalid action ${action} for node ${this.currentState.node}`) } // 执行节点逻辑 const result = await this.executeNode(currentNode, payload) // 记录历史 this.currentState.history.push({ node: this.currentState.node, action, timestamp: new Date(), data: result }) // 转移到下一个节点 this.currentState.node = transition.next return this.currentState } async executeNode(node, payload) { switch(node.type) { case 'process': return this.executeProcessNode(node, payload) case 'condition': return this.executeConditionNode(node, payload) // 其他节点类型... } } }7.3 可视化流程监控
为管理员提供流程执行的可视化监控:
<template> <div class="flow-monitor"> <super-flow :node-list="enhancedNodes" :link-list="links" :readonly="true" > <template v-slot:node="{ meta }"> <div :class="['monitor-node', meta.status]"> <div class="node-title">{{ meta.name }}</div> <div class="node-status">{{ meta.status }}</div> <div v-if="meta.status === 'completed'" class="node-time"> {{ meta.completedAt }} </div> </div> </template> </super-flow> </div> </template> <script> export default { props: ['instance'], computed: { enhancedNodes() { return this.instance.nodes.map(node => { const status = this.getInstanceStatus(node.id) return { ...node, meta: { ...node.meta, status, completedAt: this.getCompletionTime(node.id) } } }) } } } </script>8. 从原型到生产环境
将审批流原型转化为生产环境可用的系统需要考虑以下方面:
8.1 性能与稳定性优化
前端优化:
- 虚拟滚动大型流程
- 使用Web Worker处理复杂计算
- 实现增量保存机制
后端集成:
- 设计高效的流程存储结构
- 实现流程版本控制
- 提供流程实例监控接口
8.2 权限与安全控制
审批流程通常涉及敏感数据,需要严格的安全控制:
// 权限检查中间件 function checkFlowPermission(flowId, userId) { return db.flows.findOne({ _id: flowId, $or: [ { owner: userId }, { collaborators: userId }, { isPublic: true } ] }) } // 审计日志记录 function logFlowAction(action, userId, details) { db.auditLogs.insert({ action, userId, details, timestamp: new Date(), ip: getClientIp() }) }8.3 移动端适配
现代办公越来越依赖移动设备,审批流需要良好的移动端体验:
- 响应式布局:
.flow-container { width: 100%; height: 100vh; } @media (max-width: 768px) { .node-palette { position: fixed; bottom: 0; left: 0; right: 0; height: 60px; overflow-x: auto; } .flow-container { height: calc(100vh - 60px); } }- 手势操作支持:
// 触摸事件处理 function setupTouchEvents() { const flowEl = document.querySelector('.flow-container') flowEl.addEventListener('touchstart', handleTouchStart) flowEl.addEventListener('touchmove', handleTouchMove) flowEl.addEventListener('touchend', handleTouchEnd) // 实现拖拽、缩放等手势逻辑 }- 离线功能:
// 使用Service Worker缓存流程模板 self.addEventListener('install', event => { event.waitUntil( caches.open('flow-templates').then(cache => { return cache.addAll([ '/api/templates/basic', '/api/templates/leave', '/api/templates/expense' ]) }) ) })9. 测试策略与质量保障
确保审批流程系统的可靠性需要全面的测试方案:
9.1 单元测试重点
- 流程验证逻辑:
describe('流程验证', () => { it('应检测孤立节点', () => { const flow = { nodes: [{id: 'node1'}, {id: 'node2'}], links: [] } expect(validateFlow(flow)).toHaveProperty('errors') }) it('应通过有效流程', () => { const flow = { nodes: [{id: 'start'}, {id: 'approve'}], links: [{source: 'start', target: 'approve'}] } expect(validateFlow(flow).errors).toHaveLength(0) }) })- 条件节点逻辑:
describe('条件节点', () => { const conditionNode = { type: 'condition', meta: { rules: [ {when: 'amount > 10000', next: 'manager_approve'}, {default: 'normal_approve'} ] } } it('应匹配金额条件', () => { const context = {amount: 15000} expect(evaluateCondition(conditionNode, context)) .toBe('manager_approve') }) it('应回退到默认分支', () => { const context = {amount: 5000} expect(evaluateCondition(conditionNode, context)) .toBe('normal_approve') }) })9.2 端到端测试场景
使用Cypress或Playwright测试完整用户旅程:
describe('请假审批流程', () => { it('应完成简单审批流程', () => { cy.visit('/flow/leave') cy.dragNode('start', 100, 50) cy.dragNode('process', 100, 200) cy.connectNodes('start', 'process') cy.getNode('process').dblclick() cy.selectApprover('department_manager') cy.saveFlow('test_leave_flow') cy.submitTestInstance() cy.approveAs('department_manager') cy.assertFlowCompleted() }) })9.3 性能测试指标
建立关键性能基准:
| 场景 | 节点数量 | 可接受响应时间 |
|---|---|---|
| 小型流程 | <10节点 | <500ms |
| 中型流程 | 10-50节点 | <1s |
| 大型流程 | 50+节点 | <2s |
// 性能测试示例 describe('大型流程性能', () => { before(() => { cy.generateLargeFlow(100) // 生成100个节点的测试流程 }) it('应在2秒内渲染完成', () => { cy.visit('/flow/large') cy.get('.loading-indicator').should('not.exist') cy.get('.flow-container').should('be.visible') cy.window().then(win => { expect(win.performance.timing.loadEventEnd - win.performance.timing.navigationStart) .to.be.lessThan(2000) }) }) })10. 持续演进与扩展
随着业务发展,审批流系统需要不断进化:
10.1 插件机制设计
允许通过插件扩展功能:
// 插件接口设计 class FlowPlugin { constructor(flowEditor) { this.editor = flowEditor } install() { // 注册自定义节点类型 // 添加工具栏按钮 // 扩展右键菜单 } } // 示例插件:会签功能 class ParallelApprovalPlugin extends FlowPlugin { install() { this.editor.registerNodeType('parallel', { template: ParallelApprovalNode, validator: (node) => { return node.meta.approvers?.length > 0 } }) this.editor.addToolbarButton({ icon: 'users', action: () => this.addParallelNode() }) } addParallelNode() { this.editor.addNode({ type: 'parallel', meta: { name: '会签审批', approvers: [], required: 1 // 需要几人同意 } }) } }10.2 与现有系统集成
常见集成点及实现方式:
- 组织架构同步:
async syncDepartments() { const depts = await HRSystem.getDepartments() this.departments = depts.map(dept => ({ id: dept.code, name: dept.name, manager: dept.managerId })) }- 单点登录集成:
// 认证中间件 function ssoMiddleware(req, res, next) { const token = req.headers['authorization'] if (!token) return res.status(401).send() SSO.validate(token).then(user => { req.user = user next() }).catch(() => res.status(403).send()) }- 消息通知对接:
class NotificationService { constructor() { this.providers = { email: new EmailProvider(), sms: new SmsProvider(), app: new MobileAppPush() } } send(recipient, message, channels = ['app']) { return Promise.all( channels.map(channel => this.providers[channel].send(recipient, message) ) ) } }10.3 智能化方向探索
引入AI技术提升审批流智能化水平:
- 智能路由建议:
function suggestApprovers(context) { return AI.predict('approver_suggestion', { applicant: context.applicant, request_type: context.type, historical_patterns: context.history }) }- 自动审批规则:
function evaluateAutoApproval(request) { return AI.predict('approval_likelihood', { request, applicant_history: request.applicant.approvalHistory, company_policy: currentPolicy }).then(score => score > 0.9) }- 异常检测:
function detectAnomalies(request) { return AI.detect('approval_anomalies', { current_request: request, historical_requests: similarRequests, applicant_behavior: request.applicant.requestPatterns }) }11. 团队协作与版本管理
多人协作设计审批流程需要专门的版本管理方案:
11.1 基于Git的流程版本控制
将流程定义存储在Git仓库中:
/workflows /leave v1.yaml v2.yaml /expense v1.yaml v2.yaml# 典型工作流程 git checkout -b feature/new-approval-flow # 编辑流程文件 git add . git commit -m "Add finance approval step" git push origin feature/new-approval-flow # 创建Pull Request进行代码审查11.2 变更影响分析
在修改流程时分析潜在影响:
function analyzeImpact(flow, change) { // 识别受影响的节点 const affectedNodes = findDependentNodes(flow, change) // 检查正在运行的实例 const runningInstances = db.instances.find({ flowId: flow.id, status: 'running', currentNode: { $in: affectedNodes } }) return { affectedNodes, runningInstances: runningInstances.count(), completedInstances: db.instances.find({ flowId: flow.id, status: 'completed', path: { $elemMatch: { nodeId: { $in: affectedNodes } } } }).count() } }11.3 审批流设计规范
建立团队设计规范确保一致性:
命名约定:
- 节点ID:
类型_描述,如approve_dept - 变量名:
camelCase - 条件表达式:
field operator value,如amount > 10000
- 节点ID:
文档标准:
- 每个流程顶部添加注释说明用途
- 复杂节点添加业务逻辑说明
- 维护变更日志
# 采购审批流程 # 用途:所有采购申请的审批流程 # 创建人:张三 # 最后更新:2023-06-15 flow: version: 2.1 changelog: - version: 2.1 date: 2023-06-15 changes: 增加财务复核节点 - version: 2.0 date: 2023-03-10 changes: 拆分大额采购分支 start: submit nodes: submit: type: start description: 申请人填写采购申请单12. 用户体验优化技巧
提升审批流系统的用户体验可以显著提高采用率:
12.1 直观的视觉反馈
- 节点状态可视化:
/* 不同状态的节点样式 */ .node-status-pending { border: 1px dashed #ccc; background-color: #f9f9f9; } .node-status-processing { border: 2px solid #409EFF; animation: pulse 1.5s infinite; } @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(64, 158, 255, 0.4); } 70% { box-shadow: 0 0 0 10px rgba(64, 158, 255, 0); } 100% { box-shadow: 0 0 0 0 rgba(64, 158, 255, 0); } }- 连接线动画:
// 动态连线效果 function animateConnection(link) { const path = link.getPathElement() const length = path.getTotalLength() path.style.strokeDasharray = length path.style.strokeDashoffset = length const animation = path.animate( [{ strokeDashoffset: length }, { strokeDashoffset: 0 }], { duration: 1000, fill: 'forwards' } ) return animation.finished }12.2 快捷操作设计
提高频繁操作的效率:
- 键盘快捷键:
// 快捷键设置 document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.key === 's') { e.preventDefault() this.saveFlow() } if (e.key === 'Delete') { this.deleteSelected() } })