UNPKG

vue-flow-editor

Version:

vue-flow-editor 是基于 Vue2.0 以及 G6@3.1.10 实现的流程编辑器。在线操作地址:

281 lines (248 loc) 12.3 kB
<html> <head> <meta charset="utf-8"> <title>vue-flow-editor demo</title> <link type="text/css" rel="stylesheet" href="http://martsforever-pot.gitee.io/vue-flow-editor/dist/vue-flow-editor.min.css?v=0.2.3"> <link rel="stylesheet" href="http://martsforever-pot.gitee.io/vue-flow-editor/lib/element-ui.css"> <style> html, body { padding: 0; margin: 0; } </style> </head> <body> <div id="app"> </div> <script src="./lib/g6.umd.min.js"></script> <script src="http://martsforever-pot.gitee.io/vue-flow-editor/lib/vue.min.js"></script> <script src="http://martsforever-pot.gitee.io/vue-flow-editor/lib/vue-composition-api/vue-composition-api.umd.js"></script> <script src="http://martsforever-pot.gitee.io/vue-flow-editor/lib/element-ui.js"></script> <script src="http://martsforever-pot.gitee.io/vue-flow-editor/dist/vue-flow-editor.min.js?v=0.2.3"></script> <script> console.log(window) Vue.use(window.vueCompositionApi.default) Vue.use(window.VueFlowEditor.default) const Ele = window.ELEMENT const G6 = window.G6.default const reactive = window.vueCompositionApi.reactive function delay(time) { return new Promise((resolve) => setTimeout(resolve, time)) } const AppData = { "nodes": [{"data": {}, "id": "start-node", "label": "开始", "shape": "ellipse", "size": [120, 40], "x": 380, "y": 100}, {"data": {}, "id": "1588848310120", "label": "主管审批", "shape": "rect", "size": [120, 40], "x": 380, "y": 180}, {"data": {}, "id": "1588848322179", "label": "经理审批", "shape": "rect", "size": [120, 40], "x": 380, "y": 260}, { "data": {}, "id": "1588848338211", "label": "金额>2万", "shape": "diamond", "size": [120, 40], "x": 380, "y": 340 }, {"data": {}, "id": "1588848351476", "label": "财务打款", "shape": "rect", "size": [120, 40], "x": 380, "y": 460}, {"data": {}, "id": "end-node", "label": "结束", "shape": "ellipse", "size": [120, 40], "x": 380, "y": 540}, {"data": {}, "id": "1588848397511", "label": "VIP审批", "shape": "rect", "size": [120, 40], "x": 620, "y": 340}, { "data": {}, "id": "1588848436694", "label": "金额>10万", "shape": "diamond", "size": [120, 40], "x": 780, "y": 340 }, {"data": {}, "id": "1588848449431", "label": "CEO审批", "shape": "rect", "size": [120, 40], "x": 980, "y": 460}], "edges": [{"shape": "flow-polyline-round", "source": "start-node", "sourceAnchor": 2, "target": "1588848310120", "targetAnchor": 0}, {"shape": "flow-polyline-round", "source": "1588848310120", "sourceAnchor": 2, "target": "1588848322179", "targetAnchor": 0}, { "shape": "flow-polyline-round", "source": "1588848322179", "sourceAnchor": 2, "target": "1588848338211", "targetAnchor": 0 }, {"shape": "flow-polyline-round", "source": "1588848338211", "sourceAnchor": 2, "target": "1588848351476", "targetAnchor": 0, "label": "否"}, {"shape": "flow-polyline-round", "source": "1588848351476", "sourceAnchor": 2, "target": "end-node", "targetAnchor": 0}, { "shape": "flow-polyline-round", "source": "1588848338211", "sourceAnchor": 3, "target": "1588848397511", "targetAnchor": 1, "label": "是" }, {"shape": "flow-polyline-round", "source": "1588848436694", "sourceAnchor": 3, "target": "1588848449431", "targetAnchor": 0, "label": "是"}, {"shape": "flow-polyline-round", "source": "1588848449431", "sourceAnchor": 1, "target": "1588848351476", "targetAnchor": 3}, { "shape": "flow-polyline-round", "source": "1588848436694", "sourceAnchor": 2, "target": "1588848351476", "targetAnchor": 3, "label": "否" }, {"shape": "flow-polyline-round", "source": "1588848397511", "sourceAnchor": 3, "target": "1588848436694", "targetAnchor": 1}] } new Vue({ el: '#app', template: ` <div class="app" style="height: 100vh"> <vue-flow-editor ref="editor" :data="state.data" :grid="showGrid" :miniMap="showMiniMap" :onRef="onRef" :multipleSelect="true" :loading="state.editorLoading" :disabledUndo="false" :disabledDragEdge="true" :beforeDelete="handleBeforeDelete" :afterDelete="handleAfterDelete" :beforeAdd="handleBeforeAdd" :afterAdd="handleAfterAdd" @dblclick-node="onDblclickNode" @dblclick-edge="onDblClickEdge" > <div slot="menu"> <vue-flow-edit-menu-group v-for="(group,groupIndex) in state.menuData" :label="group.label" :key="groupIndex"> <vue-flow-edit-menu v-for="(menu,menuIndex) in group.menus" :key="menuIndex" :model="menu"/> </vue-flow-edit-menu-group> </div> <div slot="model" style="padding: 16px"> <el-form v-if="!!state.detailModel" ref="form" :model="state.detailModel" label-width="100px"> <el-form-item label="节点名称" prop="label"> <el-input v-model="state.detailModel.label"/> </el-form-item> <template v-if="state.detailModel.type !== 'edge'"> <el-form-item label="节点背景色" prop="style.fill"> <el-color-picker v-model="state.detailModel.style.fill"/> </el-form-item> <el-form-item label="节点边框色" prop="style.stroke"> <el-color-picker v-model="state.detailModel.style.stroke"/> </el-form-item> <el-form-item label="节点文字色" prop="labelCfg.style.stroke"> <el-color-picker v-model="state.detailModel.labelCfg.style.fill"/> </el-form-item> </template> </el-form> </div> <template slot="toolbar"> <el-tooltip content="测试工具栏插槽"> <div class="vue-flow-editor-toolbar-item"> <i class="el-icon-search"/> </div> </el-tooltip> </template> <template slot="foot"> <el-button type="primary" @click="save">保存</el-button> <el-button @click="cancel">取消</el-button> </template> </vue-flow-editor> </div> `, setup() { const state = reactive({ data: AppData, detailModel: null, editorLoading: false, selectOptions: [ {label: '待确认', value: '0'}, {label: '填写表单', value: '1'}, {label: '部门负责人审批', value: '2'}, {label: '总经理审批', value: '3'}, ], menuData: [ { label: '流程节点', menus: [ {label: '开始', shape: 'ellipse', id: 'start-node'}, {label: '结束', shape: 'ellipse', id: 'end-node'}, {label: '审批节点', busType: '123'}, {label: '判断节点'}, ], }, { label: '其他形状节点', menus: [ {label: '矩形节点', shape: 'rect'}, {label: '圆形节点', shape: 'circle'}, {label: '椭圆节点', shape: 'ellipse'}, {label: '菱形节点', shape: 'diamond'}, {label: '三角形节点', shape: 'triangle'}, {label: '星形节点', shape: 'star'}, ] } ] }) let editor; function onDblclickNode(e) { const model = G6.Util.clone(e.item.get('model')) model.style = model.style || {} model.labelCfg = model.labelCfg || {style: {}} state.detailModel = model editor.openModel() } function onDblClickEdge(e) { const {source, target, style, labelCfg, label} = e.item.get('model') const model = {label, source, target, style: style || {}, labelCfg: labelCfg || {style: {}}, type: null, id: null} model.type = e.item.get('type') model.id = e.item.get('id') state.detailModel = model editor.openModel() } function cancel() { editor.closeModel() } function save() { editor.updateModel(state.detailModel) editor.closeModel() } async function handleBeforeDelete(model, type) { if (type === 'node') { if (model.label === '开始') { state.editorLoading = true await delay(1000) state.editorLoading = false Ele.Notification.error('不可以删除【开始】节点') return Promise.reject('reject') } } } function handleAfterDelete(model, type) { if (type === 'edge') { console.log('delete edge') } else { console.log('after delete', model.label, {...model}) } } function handleBeforeAdd(model, type) { if (type === 'edge') { if (model.source === 'end-node') { Ele.Notification.error('结束节点不能输出连线其他节点') return Promise.reject('reject') } } if (type === 'node') { if (model.id === 'start-node' || model.id === 'end-node') { const data = editor.editorState.graph.save() for (let i = 0; i < data.nodes.length; i++) { const node = data.nodes[i]; if (node.id === model.id) { Ele.Notification.error(`只能有一个${model.id === 'start-node' ? '开始' : '结束'}节点`) return Promise.reject('reject') } } } } } function handleAfterAdd(model, type) { if (type === 'edge') { console.log(`新增连接线`) } } return { state, showGrid: true, showMiniMap: false, onDblclickNode, onDblClickEdge, cancel, save, handleBeforeDelete, handleAfterDelete, handleBeforeAdd, handleAfterAdd, onRef: e => editor = e, } }, }).$mount() </script> </body> </html>