赞
踩
自己设计的流程图 vue2加element-ui 有类似可以直接修改拿去用 每个流程都可以配置,只是流程线没加配置 有需要流程线配置可以看官文档 https://x6.antv.vision/zh/docs/tutorial/basic/graph 我用的antv/x6版本是"@antv/x6": “~1.16.0”,
<template> <div> <el-button style="float: right; margin-top: -70px; margin-right: 180px" size="mini" type="primary" @click="saveFlow" > 保存 </el-button> <el-button style="float: right; margin-top: -70px; margin-right: 100px" size="mini" type="primary" @click="$router.back(-1)" > 返回 </el-button> <el-container class="process-edit-container"> <!-- 顶部功能区域 --> <el-header> <el-form ref="workflow" :model="workflow" :rules="rules" label-width="80px" style="display: flex"> <el-form-item label="名称" prop="name" style="width: 300px"> <el-input v-model="workflow.name" size="mini" :disabled="disables" /> </el-form-item> <el-form-item label="描述" prop="description" style="width: 300px"> <el-input v-model="workflow.description" size="mini" /> </el-form-item> <el-form-item label="产品线" prop="productLine" style="width: 300px"> <el-select v-model="workflow.productLine" clearable filterable placeholder="请选择产品线" size="mini" style="width: 100%" :disabled="disables" @change="getJobName" > <el-option v-for="(option, index) in [ { label: 'mall', value: 1 }, { label: 'store', value: 2 } ]" :key="index" :label="option.label" :value="option.value" /> </el-select> </el-form-item> </el-form> </el-header> <el-container> <!-- 左侧组件区域 --> <el-aside width="200px"> <div class="process-component-list"> <img data-color="#FA8C16" data-label="开始" data-shape="circle" data-size="72*72" data-type="node" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBpZD0ic3RhcnRfbm9kZSIgd2lkdGg9IjgwcHgiIGhlaWdodD0iODBweCIgdmlld0JveD0iMCAwIDgwIDgwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA0OS4xICg1MTE0NykgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+R3JvdXAgMjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPgogICAgICAgIDxjaXJjbGUgaWQ9InBhdGgtMSIgY3g9IjM2IiBjeT0iMzYiIHI9IjM2Ij48L2NpcmNsZT4KICAgICAgICA8ZmlsdGVyIHg9Ii05LjclIiB5PSItNi45JSIgd2lkdGg9IjExOS40JSIgaGVpZ2h0PSIxMTkuNCUiIGZpbHRlclVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgaWQ9ImZpbHRlci0yIj4KICAgICAgICAgICAgPGZlT2Zmc2V0IGR4PSIwIiBkeT0iMiIgaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9InNoYWRvd09mZnNldE91dGVyMSI+PC9mZU9mZnNldD4KICAgICAgICAgICAgPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMiIgaW49InNoYWRvd09mZnNldE91dGVyMSIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIj48L2ZlR2F1c3NpYW5CbHVyPgogICAgICAgICAgICA8ZmVDb21wb3NpdGUgaW49InNoYWRvd0JsdXJPdXRlcjEiIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJvdXQiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSI+PC9mZUNvbXBvc2l0ZT4KICAgICAgICAgICAgPGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDAgICAwIDAgMCAwIDAgICAwIDAgMCAwIDAgIDAgMCAwIDAuMDQgMCIgdHlwZT0ibWF0cml4IiBpbj0ic2hhZG93Qmx1ck91dGVyMSI+PC9mZUNvbG9yTWF0cml4PgogICAgICAgIDwvZmlsdGVyPgogICAgPC9kZWZzPgogICAgPGcgaWQ9IlBhZ2UtMSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IuWfuuehgOa1geeoi+Wbvi0wMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEwNi4wMDAwMDAsIC05My4wMDAwMDApIj4KICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDExMC4wMDAwMDAsIDk1LjAwMDAwMCkiPgogICAgICAgICAgICAgICAgPGcgaWQ9Ik92YWwiPgogICAgICAgICAgICAgICAgICAgIDx1c2UgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMSIgZmlsdGVyPSJ1cmwoI2ZpbHRlci0yKSIgeGxpbms6aHJlZj0iI3BhdGgtMSI+PC91c2U+CiAgICAgICAgICAgICAgICAgICAgPHVzZSBmaWxsLW9wYWNpdHk9IjAuOTIiIGZpbGw9IiNGRkYyRTgiIGZpbGwtcnVsZT0iZXZlbm9kZCIgeGxpbms6aHJlZj0iI3BhdGgtMSI+PC91c2U+CiAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSBzdHJva2U9IiNGRkMwNjkiIHN0cm9rZS13aWR0aD0iMSIgY3g9IjM2IiBjeT0iMzYiIHI9IjM1LjUiPjwvY2lyY2xlPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPHRleHQgaWQ9IuW8gOWni+iKgueCuSIgZm9udC1mYW1pbHk9IlBpbmdGYW5nU0MtUmVndWxhciwgUGluZ0ZhbmcgU0MiIGZvbnQtc2l6ZT0iMTIiIGZvbnQtd2VpZ2h0PSJub3JtYWwiIGxpbmUtc3BhY2luZz0iMTIiIGZpbGw9IiMwMDAwMDAiIGZpbGwtb3BhY2l0eT0iMC42NSI+CiAgICAgICAgICAgICAgICAgICAgPHRzcGFuIHg9IjEyIiB5PSI0MSI+5byA5aeL6IqC54K5PC90c3Bhbj4KICAgICAgICAgICAgICAgIDwvdGV4dD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+" @mousedown="addNodeToGraph" > <img draggable="false" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iODhweCIgaGVpZ2h0PSI1NnB4IiB2aWV3Qm94PSIwIDAgODggNTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQ5LjEgKDUxMTQ3KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5Hcm91cDwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPgogICAgICAgIDxyZWN0IGlkPSJwYXRoLTEiIHg9IjAiIHk9IjAiIHdpZHRoPSI4MCIgaGVpZ2h0PSI0OCIgcng9IjQiPjwvcmVjdD4KICAgICAgICA8ZmlsdGVyIHg9Ii04LjglIiB5PSItMTAuNCUiIHdpZHRoPSIxMTcuNSUiIGhlaWdodD0iMTI5LjIlIiBmaWx0ZXJVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giIGlkPSJmaWx0ZXItMiI+CiAgICAgICAgICAgIDxmZU9mZnNldCBkeD0iMCIgZHk9IjIiIGluPSJTb3VyY2VBbHBoYSIgcmVzdWx0PSJzaGFkb3dPZmZzZXRPdXRlcjEiPjwvZmVPZmZzZXQ+CiAgICAgICAgICAgIDxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiIGluPSJzaGFkb3dPZmZzZXRPdXRlcjEiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSI+PC9mZUdhdXNzaWFuQmx1cj4KICAgICAgICAgICAgPGZlQ29tcG9zaXRlIGluPSJzaGFkb3dCbHVyT3V0ZXIxIiBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0ib3V0IiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb21wb3NpdGU+CiAgICAgICAgICAgIDxmZUNvbG9yTWF0cml4IHZhbHVlcz0iMCAwIDAgMCAwICAgMCAwIDAgMCAwICAgMCAwIDAgMCAwICAwIDAgMCAwLjA0IDAiIHR5cGU9Im1hdHJpeCIgaW49InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb2xvck1hdHJpeD4KICAgICAgICA8L2ZpbHRlcj4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSLln7rnoYDmtYHnqIvlm74tMDEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC02LjAwMDAwMCwgLTEwNS4wMDAwMDApIj4KICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMC4wMDAwMDAsIDEwNy4wMDAwMDApIj4KICAgICAgICAgICAgICAgIDxnIGlkPSJSZWN0YW5nbGUtMTUtQ29weSI+CiAgICAgICAgICAgICAgICAgICAgPHVzZSBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIxIiBmaWx0ZXI9InVybCgjZmlsdGVyLTIpIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICA8dXNlIGZpbGwtb3BhY2l0eT0iMC45MiIgZmlsbD0iI0U2RjdGRiIgZmlsbC1ydWxlPSJldmVub2RkIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICA8cmVjdCBzdHJva2U9IiMxODkwRkYiIHN0cm9rZS13aWR0aD0iMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI4MiIgaGVpZ2h0PSI0NyIgcng9IjQiPjwvcmVjdD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDx0ZXh0IGlkPSLlrqHmibnoioLngrkiIGZvbnQtZmFtaWx5PSJQaW5nRmFuZ1NDLVJlZ3VsYXIsIFBpbmdGYW5nIFNDIiBmb250LXNpemU9IjEyIiBmb250LXdlaWdodD0ibm9ybWFsIiBsaW5lLXNwYWNpbmc9IjEyIiBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjAuNjUiPgogICAgICAgICAgICAgICAgICAgIDx0c3BhbiB4PSI2IiB5PSIyNiI+SmVua2luc19UYXNrPC90c3Bhbj4KICAgICAgICAgICAgICAgIDwvdGV4dD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+" data-type="node" data-shape="rect" data-size="90*48" data-color="#1890FF" data-label="Jenkins_Task" @mousedown="addNodeToGraph" > <img draggable="false" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iODhweCIgaGVpZ2h0PSI1NnB4IiB2aWV3Qm94PSIwIDAgODggNTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQ5LjEgKDUxMTQ3KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5Hcm91cDwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPgogICAgICAgIDxyZWN0IGlkPSJwYXRoLTEiIHg9IjAiIHk9IjAiIHdpZHRoPSI4MCIgaGVpZ2h0PSI0OCIgcng9IjQiPjwvcmVjdD4KICAgICAgICA8ZmlsdGVyIHg9Ii04LjglIiB5PSItMTAuNCUiIHdpZHRoPSIxMTcuNSUiIGhlaWdodD0iMTI5LjIlIiBmaWx0ZXJVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giIGlkPSJmaWx0ZXItMiI+CiAgICAgICAgICAgIDxmZU9mZnNldCBkeD0iMCIgZHk9IjIiIGluPSJTb3VyY2VBbHBoYSIgcmVzdWx0PSJzaGFkb3dPZmZzZXRPdXRlcjEiPjwvZmVPZmZzZXQ+CiAgICAgICAgICAgIDxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiIGluPSJzaGFkb3dPZmZzZXRPdXRlcjEiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSI+PC9mZUdhdXNzaWFuQmx1cj4KICAgICAgICAgICAgPGZlQ29tcG9zaXRlIGluPSJzaGFkb3dCbHVyT3V0ZXIxIiBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0ib3V0IiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb21wb3NpdGU+CiAgICAgICAgICAgIDxmZUNvbG9yTWF0cml4IHZhbHVlcz0iMCAwIDAgMCAwICAgMCAwIDAgMCAwICAgMCAwIDAgMCAwICAwIDAgMCAwLjA0IDAiIHR5cGU9Im1hdHJpeCIgaW49InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb2xvck1hdHJpeD4KICAgICAgICA8L2ZpbHRlcj4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSLln7rnoYDmtYHnqIvlm74tMDEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC02LjAwMDAwMCwgLTEwNS4wMDAwMDApIj4KICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMC4wMDAwMDAsIDEwNy4wMDAwMDApIj4KICAgICAgICAgICAgICAgIDxnIGlkPSJSZWN0YW5nbGUtMTUtQ29weSI+CiAgICAgICAgICAgICAgICAgICAgPHVzZSBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIxIiBmaWx0ZXI9InVybCgjZmlsdGVyLTIpIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICA8dXNlIGZpbGwtb3BhY2l0eT0iMC45MiIgZmlsbD0iI0U2RkZGQiIgZmlsbC1ydWxlPSJldmVub2RkIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICA8cmVjdCBzdHJva2U9IiMxODkwRkYiIHN0cm9rZS13aWR0aD0iMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI3OSIgaGVpZ2h0PSI0NyIgcng9IjQiPjwvcmVjdD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDx0ZXh0IGlkPSLlrqHmibnoioLngrkiIGZvbnQtZmFtaWx5PSJQaW5nRmFuZ1NDLVJlZ3VsYXIsIFBpbmdGYW5nIFNDIiBmb250LXNpemU9IjEyIiBmb250LXdlaWdodD0ibm9ybWFsIiBsaW5lLXNwYWNpbmc9IjEyIiBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjAuNjUiPgogICAgICAgICAgICAgICAgICAgIDx0c3BhbiB4PSI2IiB5PSIyNiI+QWlyZmxvd19UYXNrPC90c3Bhbj4KICAgICAgICAgICAgICAgIDwvdGV4dD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+" data-type="node" data-shape="rect" data-size="90*48" data-color="#13C2C2" data-label="Airflow_Task" @mousedown="addNodeToGraph" > <img draggable="false" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iODhweCIgaGVpZ2h0PSI1NnB4IiB2aWV3Qm94PSIwIDAgODggNTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQ5LjEgKDUxMTQ3KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5Hcm91cDwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPgogICAgICAgIDxyZWN0IGlkPSJwYXRoLTEiIHg9IjAiIHk9IjAiIHdpZHRoPSI4MCIgaGVpZ2h0PSI0OCIgcng9IjQiPjwvcmVjdD4KICAgICAgICA8ZmlsdGVyIHg9Ii04LjglIiB5PSItMTAuNCUiIHdpZHRoPSIxMTcuNSUiIGhlaWdodD0iMTI5LjIlIiBmaWx0ZXJVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giIGlkPSJmaWx0ZXItMiI+CiAgICAgICAgICAgIDxmZU9mZnNldCBkeD0iMCIgZHk9IjIiIGluPSJTb3VyY2VBbHBoYSIgcmVzdWx0PSJzaGFkb3dPZmZzZXRPdXRlcjEiPjwvZmVPZmZzZXQ+CiAgICAgICAgICAgIDxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiIGluPSJzaGFkb3dPZmZzZXRPdXRlcjEiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSI+PC9mZUdhdXNzaWFuQmx1cj4KICAgICAgICAgICAgPGZlQ29tcG9zaXRlIGluPSJzaGFkb3dCbHVyT3V0ZXIxIiBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0ib3V0IiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb21wb3NpdGU+CiAgICAgICAgICAgIDxmZUNvbG9yTWF0cml4IHZhbHVlcz0iMCAwIDAgMCAwICAgMCAwIDAgMCAwICAgMCAwIDAgMCAwICAwIDAgMCAwLjA0IDAiIHR5cGU9Im1hdHJpeCIgaW49InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb2xvck1hdHJpeD4KICAgICAgICA8L2ZpbHRlcj4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSLln7rnoYDmtYHnqIvlm74tMDEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC02LjAwMDAwMCwgLTEwNS4wMDAwMDApIj4KICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMC4wMDAwMDAsIDEwNy4wMDAwMDApIj4KICAgICAgICAgICAgICAgIDxnIGlkPSJSZWN0YW5nbGUtMTUtQ29weSI+CiAgICAgICAgICAgICAgICAgICAgPHVzZSBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIxIiBmaWx0ZXI9InVybCgjZmlsdGVyLTIpIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICA8dXNlIGZpbGwtb3BhY2l0eT0iMC45MiIgZmlsbD0iIzAwNzc5OSIgZmlsbC1ydWxlPSJldmVub2RkIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICA8cmVjdCBzdHJva2U9IiMxODkwRkYiIHN0cm9rZS13aWR0aD0iMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI3OSIgaGVpZ2h0PSI0NyIgcng9IjQiPjwvcmVjdD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDx0ZXh0IGlkPSLlrqHmibnoioLngrkiIGZvbnQtZmFtaWx5PSJQaW5nRmFuZ1NDLVJlZ3VsYXIsIFBpbmdGYW5nIFNDIiBmb250LXNpemU9IjEyIiBmb250LXdlaWdodD0ibm9ybWFsIiBsaW5lLXNwYWNpbmc9IjEyIiBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjAuNjUiPgogICAgICAgICAgICAgICAgICAgIDx0c3BhbiB4PSI5IiB5PSIyNiI+U2hlbGxfVGFzazwvdHNwYW4+CiAgICAgICAgICAgICAgICA8L3RleHQ+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==" data-type="node" data-shape="rect" data-size="90*48" data-color="#008080" data-label="Shell_Task" @mousedown="addNodeToGraph" > <img draggable="false" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iODhweCIgaGVpZ2h0PSI1NnB4IiB2aWV3Qm94PSIwIDAgODggNTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQ5LjEgKDUxMTQ3KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5Hcm91cDwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPgogICAgICAgIDxyZWN0IGlkPSJwYXRoLTEiIHg9IjAiIHk9IjAiIHdpZHRoPSI4MCIgaGVpZ2h0PSI0OCIgcng9IjQiPjwvcmVjdD4KICAgICAgICA8ZmlsdGVyIHg9Ii04LjglIiB5PSItMTAuNCUiIHdpZHRoPSIxMTcuNSUiIGhlaWdodD0iMTI5LjIlIiBmaWx0ZXJVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giIGlkPSJmaWx0ZXItMiI+CiAgICAgICAgICAgIDxmZU9mZnNldCBkeD0iMCIgZHk9IjIiIGluPSJTb3VyY2VBbHBoYSIgcmVzdWx0PSJzaGFkb3dPZmZzZXRPdXRlcjEiPjwvZmVPZmZzZXQ+CiAgICAgICAgICAgIDxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiIGluPSJzaGFkb3dPZmZzZXRPdXRlcjEiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSI+PC9mZUdhdXNzaWFuQmx1cj4KICAgICAgICAgICAgPGZlQ29tcG9zaXRlIGluPSJzaGFkb3dCbHVyT3V0ZXIxIiBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0ib3V0IiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb21wb3NpdGU+CiAgICAgICAgICAgIDxmZUNvbG9yTWF0cml4IHZhbHVlcz0iMCAwIDAgMCAwICAgMCAwIDAgMCAwICAgMCAwIDAgMCAwICAwIDAgMCAwLjA0IDAiIHR5cGU9Im1hdHJpeCIgaW49InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb2xvck1hdHJpeD4KICAgICAgICA8L2ZpbHRlcj4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSLln7rnoYDmtYHnqIvlm74tMDEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC02LjAwMDAwMCwgLTEwNS4wMDAwMDApIj4KICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMC4wMDAwMDAsIDEwNy4wMDAwMDApIj4KICAgICAgICAgICAgICAgIDxnIGlkPSJSZWN0YW5nbGUtMTUtQ29weSI+CiAgICAgICAgICAgICAgICAgICAgPHVzZSBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIxIiBmaWx0ZXI9InVybCgjZmlsdGVyLTIpIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICA8dXNlIGZpbGwtb3BhY2l0eT0iMC45MiIgZmlsbD0iI0ZGRkYwMCIgZmlsbC1ydWxlPSJldmVub2RkIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICA8cmVjdCBzdHJva2U9IiMxODkwRkYiIHN0cm9rZS13aWR0aD0iMSIgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSI3OSIgaGVpZ2h0PSI0NyIgcng9IjQiPjwvcmVjdD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDx0ZXh0IGlkPSLlrqHmibnoioLngrkiIGZvbnQtZmFtaWx5PSJQaW5nRmFuZ1NDLVJlZ3VsYXIsIFBpbmdGYW5nIFNDIiBmb250LXNpemU9IjEyIiBmb250LXdlaWdodD0ibm9ybWFsIiBsaW5lLXNwYWNpbmc9IjEyIiBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjAuNjUiPgogICAgICAgICAgICAgICAgICAgIDx0c3BhbiB4PSI3IiB5PSIyNiI+UHl0aG9uX1Rhc2s8L3RzcGFuPgogICAgICAgICAgICAgICAgPC90ZXh0PgogICAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=" data-type="node" data-shape="rect" data-size="90*48" data-color="#66FF00" data-label="Python_Task" @mousedown="addNodeToGraph" > <img draggable="false" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iODhweCIgaGVpZ2h0PSI1NnB4IiB2aWV3Qm94PSIwIDAgODggNTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQ5LjEgKDUxMTQ3KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5Hcm91cCA0PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+CiAgICAgICAgPHJlY3QgaWQ9InBhdGgtMSIgeD0iMCIgeT0iMCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjQ4IiByeD0iMjQiPjwvcmVjdD4KICAgICAgICA8ZmlsdGVyIHg9Ii04LjglIiB5PSItMTAuNCUiIHdpZHRoPSIxMTcuNSUiIGhlaWdodD0iMTI5LjIlIiBmaWx0ZXJVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giIGlkPSJmaWx0ZXItMiI+CiAgICAgICAgICAgIDxmZU9mZnNldCBkeD0iMCIgZHk9IjIiIGluPSJTb3VyY2VBbHBoYSIgcmVzdWx0PSJzaGFkb3dPZmZzZXRPdXRlcjEiPjwvZmVPZmZzZXQ+CiAgICAgICAgICAgIDxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIiIGluPSJzaGFkb3dPZmZzZXRPdXRlcjEiIHJlc3VsdD0ic2hhZG93Qmx1ck91dGVyMSI+PC9mZUdhdXNzaWFuQmx1cj4KICAgICAgICAgICAgPGZlQ29tcG9zaXRlIGluPSJzaGFkb3dCbHVyT3V0ZXIxIiBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0ib3V0IiByZXN1bHQ9InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb21wb3NpdGU+CiAgICAgICAgICAgIDxmZUNvbG9yTWF0cml4IHZhbHVlcz0iMCAwIDAgMCAwICAgMCAwIDAgMCAwICAgMCAwIDAgMCAwICAwIDAgMCAwLjA0IDAiIHR5cGU9Im1hdHJpeCIgaW49InNoYWRvd0JsdXJPdXRlcjEiPjwvZmVDb2xvck1hdHJpeD4KICAgICAgICA8L2ZpbHRlcj4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSLln7rnoYDmtYHnqIvlm74tMDEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xMDIuMDAwMDAwLCAtMTk1LjAwMDAwMCkiPgogICAgICAgICAgICA8ZyBpZD0iR3JvdXAtNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTA2LjAwMDAwMCwgMTk3LjAwMDAwMCkiPgogICAgICAgICAgICAgICAgPGcgaWQ9IlJlY3RhbmdsZS0xNS1Db3B5LTM1Ij4KICAgICAgICAgICAgICAgICAgICA8dXNlIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjEiIGZpbHRlcj0idXJsKCNmaWx0ZXItMikiIHhsaW5rOmhyZWY9IiNwYXRoLTEiPjwvdXNlPgogICAgICAgICAgICAgICAgICAgIDx1c2UgZmlsbC1vcGFjaXR5PSIwLjkyIiBmaWxsPSIjRjlGMEZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHhsaW5rOmhyZWY9IiNwYXRoLTEiPjwvdXNlPgogICAgICAgICAgICAgICAgICAgIDxyZWN0IHN0cm9rZT0iI0IzN0ZFQiIgc3Ryb2tlLXdpZHRoPSIxIiB4PSIwLjUiIHk9IjAuNSIgd2lkdGg9Ijc5IiBoZWlnaHQ9IjQ3IiByeD0iMjMuNSI+PC9yZWN0PgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPHRleHQgaWQ9Iue7k+adn+iKgueCuSIgZm9udC1mYW1pbHk9IlBpbmdGYW5nU0MtUmVndWxhciwgUGluZ0ZhbmcgU0MiIGZvbnQtc2l6ZT0iMTIiIGZvbnQtd2VpZ2h0PSJub3JtYWwiIGxpbmUtc3BhY2luZz0iMTIiIGZpbGw9IiMwMDAwMDAiIGZpbGwtb3BhY2l0eT0iMC42NSI+CiAgICAgICAgICAgICAgICAgICAgPHRzcGFuIHg9IjE2IiB5PSIyOSI+57uT5p2f6IqC54K5PC90c3Bhbj4KICAgICAgICAgICAgICAgIDwvdGV4dD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+" data-type="node" data-shape="ellipse" data-size="80*48" data-color="#722ED1" data-label="结束" @mousedown="addNodeToGraph" > </div> </el-aside> <el-main> <div ref="processCanvas" class="process-canvas" /> </el-main> <!-- 右侧配置区域 --> <el-aside style="width: 200px; height: 700px"> <!-- <div--> <!-- style="display: inline-block;--> <!-- width: 400px; height: 650px;--> <!-- border: 1px solid #E4E7ED; padding-right: 4px;"--> <!-- >--> <!-- <el-tabs v-model="activeTab" style="height: 650px">--> <!-- <el-tab-pane label="流程信息" name="processInfo">--> <!-- </el-tab-pane>--> <!-- <el-tab-pane label="配置信息" name="cellInfo">--> <!-- <el-card shadow="never" style="height: 600px">--> <el-drawer title="节点配置信息" :visible.sync="drawer" :direction="direction" > <!-- <template #header>--> <!-- <div class="card-header">--> <!-- <span>配置信息</span>--> <!-- </div>--> <!-- </template>--> <el-form v-if="selectCell && selectCell.shape !== 'edge' && selectCell.shape !== 'circle' && selectCell.shape !== 'ellipse'" ref="node" :model="node" :rules="rules" label-width="88px" class="father" > <el-form-item label="节点名称" prop="name" style="margin-right: 20px"> <el-input v-model="node.name" size="mini" placeholder="请输入节点名称" @input="updateNodeName" /> </el-form-item> <el-form-item label="描述" prop="description" style="margin-top: -20px;margin-right: 20px"> <el-input v-model="node.description" type="textarea" size="mini" /> </el-form-item> <el-form-item label="节点类型" prop="type" style="margin-top: -20px;margin-right: 20px"> <el-select v-model.number="node.type" filterable placeholder="请选择处理人类型" size="mini" style="width: 100%" disabled @change="getJobName" > <el-option v-for="item in nodeTypeList" :key="item.type" :label="item.name" :value="item.type" /> </el-select> </el-form-item> <el-form-item label="Job组件" style="margin-top: -20px;margin-right: 20px"> <el-select v-model.number="node.component_id" filterable placeholder="请选择job" size="mini" style="width: 100%" @change="getJobParam" > <el-option v-for="item in jobList" :key="item.id" :label="item.version > 0?`${item.name}(version:${item.version})`:`${item.name}`" :value="item.id" /> </el-select> </el-form-item> <div style="margin-left: 5px"> 输入参数: </div> <el-card class="process-box" shadow="never"> <template v-for="(item, index) of node.paramList"> <div :key="index"> <!-- <el-tooltip class="item" effect="dark" :content="item.description" placement="top-start"> --> <span> {{ item.name }}:</span> <!-- </el-tooltip> --> <!-- <el-form-item :label="item.name" style="margin-top: -20px;"> --> <el-form-item style="margin-left: -50px;"> <!-- 33.3%--> <el-select v-model="item.param" filterable placeholder="请选择" size="mini" style="width: 30%;" @change="selectData" > <el-option v-for="key in [{ label: '缺省值', value: 0 }, { label: '定值', value: 1 }, { label: '节点输出值', value: 2 }, { label: '全局参数', value: 3 } ]" :key="key.value" :label="key.label" :value="key.value" /> </el-select> <!-- 58.5%--> <el-input v-if="item.param === 0 || item.param === 3" :value="String(item.defaultValue)" size="mini" style="width: 40%" disabled /> <el-input v-if="item.param === 1" v-model="item.defaultValue" size="mini" style="width: 40%" /> <!-- 28%--> <el-select v-if="item.param === 2" v-model="item.nodeName" filterable placeholder="请选择" size="mini" style="width: 32%" @change="selectJobName" > <el-option v-for="key in node.nodeViewList" :key="key.id" :label="key.name" :value="key.id" /> </el-select> <el-select v-if="item.param === 2" v-model="item.export" filterable placeholder="请选择" size="mini" style="width: 32%" > <el-option v-for="key in Object.keys(node.nowNodeExportParam)" :key="key" :label="key" :value="key" /> </el-select> <br> <span style="font-size: 12px; margin-top: -15px;">{{ item.description }}</span> </el-form-item> </div> </template> </el-card> <div style="margin: 20px 0 0 5px"> 输出参数: </div> <el-card class="export-box" shadow="never"> <span>{{ Object.keys(node.exportParam).join() }}</span> </el-card><br> </el-form> <el-form v-if="selectCell && selectCell.shape === 'edge'" ref="edge" :model="edge" :rules="rules" label-width="80px" > <el-form-item v-show="false" label="源节点" prop="sourceStatus" style="margin-top: -20px"> <el-input v-model.number="edge.sourceStatus" size="mini" placeholder="请输入源节点" readonly /> </el-form-item> <el-form-item v-show="false" label="目标节点" prop="destinationStatus" style="margin-top: -20px;"> <el-input v-model.number="edge.destinationStatus" size="mini" placeholder="请输入目标节点" readonly /> </el-form-item> </el-form> </el-drawer> <!-- </el-card>--> <!-- </el-tabs>--> </el-aside> </el-container> </el-container> </div> </template> <script> // import { getRegisterTestData, getRegisterJobName } from '@/api/integration_test'; import { Graph, Addon } from '@antv/x6'; import { deepCopy } from '../utils/utils'; const { Dnd } = Addon; export default { name: 'ProcessEdit', props: { labelType: { type: Number, default: 1, }, data: { type: Object, default: () => ({ name: '', projectId: null, description: '', icon: 'el-icon-user', iconColor: 'blue', tpl: [], flow_chart: { nodes: [], edges: [], workflow: {}, }, }), }, }, data: () => ({ cells: [], workflow: {}, internalChange: false, graph: null, dnd: null, selectCell: null, activeTab: 'processInfo', projectListTmp: [], templateList: [], node: { importParam: [{ param: '' }], }, edge: {}, rules: { name: [ { required: true, message: ' ', trigger: 'change' }, ], icon: [ { required: true, message: ' ', trigger: 'change' }, ], orderId: [ { required: true, message: ' ', trigger: 'change' }, ], participant: [ { required: true, message: ' ', trigger: 'change' }, ], type: [ { required: true, message: ' ', trigger: 'change' }, ], distributeTypeId: [ { required: true, message: ' ', trigger: 'change' }, ], attributeTypeId: [ { required: true, message: ' ', trigger: 'change' }, ], sourceStatus: [ { required: true, message: ' ', trigger: 'change' }, ], created_by: [ { required: true, message: ' ', trigger: 'change' }, ], productLine: [ { required: true, message: '请选择产品线', trigger: 'change' }, ], destinationStatus: [ { required: true, message: ' ', trigger: 'change' }, ], }, nodeTypeList: [ { type: 1, name: 'JenkinsJob' }, { type: 2, name: 'AirflowJob' }, { type: 3, name: 'ShellJob' }, { type: 4, name: 'PythonJob' }, ], componentList: [ { id: 0, name: 'mall' }, { id: 1, name: 'store' }, { id: 2, name: 'gate' }, { id: 4, name: 'airport' }, ], participantOptions: [], participantMap: {}, clickCellMap: {}, loadUserPromise: [], jobInfo: {}, selectId: -1, jobList: [], nodeList: [], jobNode: {}, jobCells: [], nodeTmpId: -1, exportParam: {}, disables: false, dialogVisible: false, drawer: false, direction: 'rtl', }), computed: { currentTemplateList() { return this.templateList.filter((tpl) => { if (!this.workflow.tpl || !Array.isArray(this.workflow.tpl) || this.workflow.tpl.length === 0) { return false; } return this.workflow.tpl.indexOf(tpl.id) >= 0; }); }, }, watch: { labelType: { handler(val) { if (val === 2) { this.disables = true; } }, immediate: true, }, data: { handler(val) { if (this.internalChange) { return; } this.workflow = { name: val.name, projectId: val.projectId, description: val.description, icon: val.icon, iconColor: val.iconColor, tpl: deepCopy(val.tpl), productLine: val.product_line, nodeConfigs: {}, edgeConfigs: {}, }; const cells = []; if (val.flow_chart && val.flow_chart.nodes && val.flow_chart.nodes.length > 0) { val.flow_chart.nodes.forEach((node) => { const temp = deepCopy(node); this.workflow.nodeConfigs[node.id] = temp.valueData; delete temp.valueData; cells.push(temp); }); } if (val.flow_chart && val.flow_chart.edges && val.flow_chart.edges.length > 0) { val.flow_chart.edges.forEach((edge) => { const temp = deepCopy(edge); this.workflow.edgeConfigs[edge.id] = temp.valueData; delete temp.valueData; cells.push(temp); }); } if (this.graph) { this.graph.fromJSON({ cells }); this.graph.centerContent(); } else { this.cells = cells; } }, deep: true, immediate: true, }, }, created() { this.workflow.name = '测试流程1'; this.workflow.productLine = 1; }, mounted() { this.initG6(); }, methods: { getRegisterTestData() { getRegisterTestData(1).then((res) => { console.log(res, 666); }, (err) => { this.$message({ type: 'error', message: err.errmsg, }); console.error(err); }); }, selectData(val) { this.selectId = val; }, getJobName() { if (!this.workflow.productLine) { return; } if (!this.node.type) { return; } // getRegisterJobName(this.workflow.productLine, this.node.type).then((res) => { // if (res) { // this.jobList = res; // } // }, (err) => { // this.$message({ // type: 'error', // message: err.errmsg, // }); // console.error(err); // }); // }); }, getJobParam(val) { this.jobList.forEach((item) => { if (item.id === val) { this.node.name = item.name; this.selectCell.setAttrs({ label: { text: this.node.name, }, }); this.nodeList.forEach((keys) => { if (this.nodeTmpId === keys.id) { const ee = keys; ee.exportParam = item.export_param; } }); this.workflow.nodeConfigs[this.nodeTmpId].paramList = item.import_param.map(val => Object.assign({}, deepCopy(val), { param: 0, nodeName: '', export: '' })); if (item.export_param) { this.workflow.nodeConfigs[this.nodeTmpId].exportParam = item.export_param; } } }); this.getNodeNameList(); }, getNodeNameList() { if (Object.keys(this.jobNode).length) { Object.keys(this.jobNode).forEach((item) => { if (this.nodeTmpId === item) { this.workflow.nodeConfigs[this.nodeTmpId].nodeViewList = this.jobNode[item]; } }); } }, selectJobName(val) { if (this.workflow.nodeConfigs[val].exportParam) { this.workflow.nodeConfigs[this.nodeTmpId].nowNodeExportParam = this.workflow.nodeConfigs[val].exportParam; } }, handleCellClick(cell) { console.log(cell, 5676); if (cell.isNode() && cell.store.data.attrs.label.text !== '开始' && cell.store.data.attrs.label.text !== '结束') { this.drawer = true; } this.activeTab = 'cellInfo'; this.selectCell = cell; const { id } = cell; const data = this.graph.toJSON(); const { cells } = data; if (cell.isNode()) { if (!this.workflow.nodeConfigs[id]) { let type = 0; if (cell.attrs.label.text === 'Jenkins_Task') { type = 1; } else if (cell.attrs.label.text === 'Airflow_Task') { type = 2; } else if (cell.attrs.label.text === 'Shell_Task') { type = 3; } else if (cell.attrs.label.text === 'Python_Task') { type = 4; } else if (cell.label === 'ellipse') { type = 5; } this.workflow.nodeConfigs[id] = { tmpId: id, name: cell.attrs.label.text, type, participant: '', description: '', paramList: [], component_id: '', import_param: {}, exportParam: {}, nowNodeExportParam: {}, nodeViewList: [], }; } this.node = this.workflow.nodeConfigs[id]; const list1 = cells.filter(item => item.shape === 'rect' && item.attrs.body.stroke === '#a2d2ff'); const list2 = cells.filter(item => item.shape === 'rect' && item.attrs.body.stroke === '#a0e6e6'); const list3 = cells.filter(item => item.shape === 'rect' && item.attrs.body.stroke === '#99cccc'); const list4 = cells.filter(item => item.shape === 'rect' && item.attrs.body.stroke === '#c1ff99'); if (this.node.name === 'Jenkins_Task') { this.node.name = `${this.node.name}${list1.length}`; this.selectCell.setAttrs({ label: { text: this.node.name, }, }); } if (this.node.name === 'Airflow_Task') { this.node.name = `${this.node.name}${list2.length}`; this.selectCell.setAttrs({ label: { text: this.node.name, }, }); } if (this.node.name === 'Shell_Task') { this.node.name = `${this.node.name}${list3.length}`; this.selectCell.setAttrs({ label: { text: this.node.name, }, }); } if (this.node.name === 'Python_Task') { this.node.name = `${this.node.name}${list4.length}`; this.selectCell.setAttrs({ label: { text: this.node.name, }, }); } this.getJobName(); this.nodeTmpId = this.node.tmpId; } else { if (!this.workflow.edgeConfigs[id]) { this.workflow.edgeConfigs[id] = { tmpId: id, // label: [], // name: '', sourceStatus: cell.source.cell, destinationStatus: cell.target.cell, // attributeTypeId: '', }; } this.edge = this.workflow.edgeConfigs[id]; } this.nodeList = deepCopy(cells); const edgeList = deepCopy(cells); edgeList.forEach((item) => { if (item.source && item.target) { this.jobNode[item.target.cell] = []; // let list = this.jobNode[item.target.cell]; let obj = {}; const nodeObj = this.nodeList.find(key => item.source.cell === key.id); obj = { name: nodeObj.attrs.label.text, id: nodeObj.id, }; if (obj.name !== '开始' && obj.name !== '结束') { this.jobNode[item.target.cell].push(obj); Object.keys(this.jobNode).forEach((key) => { if (item.source.cell === key) { this.jobNode[item.target.cell].push(...this.jobNode[key]); } }); } } }); this.getNodeNameList(); }, updateEdgeName(name) { this.selectCell.setLabels(name); }, updateNodeName(name) { this.selectCell.setAttrs({ label: { text: name, }, }); }, addNodeToGraph(event) { const target = event.currentTarget; const type = target.getAttribute('data-type'); let node = null; if (type === 'node') { const shape = target.getAttribute('data-shape'); const size = target.getAttribute('data-size'); const width = Number(size.split('*')[0]); const height = Number(size.split('*')[1]); const label = target.getAttribute('data-label'); const color = target.getAttribute('data-color'); const rgbToHex = (r, g, b) => { // eslint-disable-next-line no-bitwise const hex = ((r << 16) | (g << 8) | b).toString(16); return `#${new Array(Math.abs(hex.length - 7)).join('0')}${hex}`; }; // hex to rgb const hexToRgb = (hex) => { const rgb = []; for (let i = 1; i < 7; i += 2) { // eslint-disable-next-line radix rgb.push(parseInt(`0x${hex.slice(i, i + 2)}`)); } return rgb; }; // 计算渐变过渡色 const gradient = (startColor, endColor, step) => { // 将 hex 转换为rgb const sColor = hexToRgb(startColor); const eColor = hexToRgb(endColor); // 计算R\G\B每一步的差值 const rStep = (eColor[0] - sColor[0]) / step; const gStep = (eColor[1] - sColor[1]) / step; const bStep = (eColor[2] - sColor[2]) / step; const gradientColorArr = []; for (let i = 0; i < step; i += 1) { // 计算每一步的hex值 gradientColorArr.push(rgbToHex( // eslint-disable-next-line radix parseInt(rStep * i + sColor[0]), parseInt(gStep * i + sColor[1]), parseInt(bStep * i + sColor[2]), )); } return gradientColorArr; }; const colorList = gradient(color, '#ffffff', 10); const halfColor = colorList[9]; const fillColor = colorList[6]; const ports = { groups: { top: { position: 'top', attrs: { circle: { r: 4, magnet: true, stroke: '#31d0c6', strokeWidth: 2, fill: '#fff', }, }, }, bottom: { position: 'bottom', attrs: { circle: { r: 4, magnet: true, stroke: '#31d0c6', strokeWidth: 2, fill: '#fff', }, }, }, left: { position: 'left', attrs: { circle: { r: 4, magnet: true, stroke: '#31d0c6', strokeWidth: 2, fill: '#fff', }, }, }, right: { position: 'right', attrs: { circle: { r: 4, magnet: true, stroke: '#31d0c6', strokeWidth: 2, fill: '#fff', }, }, }, }, items: [ { id: 'top', group: 'top', }, { id: 'bottom', group: 'bottom', }, { id: 'left', group: 'left', }, { id: 'right', group: 'right', }, ], }; const nodeInfo = { shape, width, height, attrs: { label: { 'font-size': 12, text: label, fill: 'black', }, body: { stroke: fillColor, fill: halfColor, strokeWidth: 2, }, }, ports, }; if (shape === 'polygon') { nodeInfo.points = '0,10 10,0 20,10 10,20'; } node = this.graph.createNode(nodeInfo); } else { node = {}; } this.dnd.start(node, event); }, initG6() { const that = this; this.graph = new Graph({ container: that.$refs.processCanvas, autoResize: true, connecting: { snap: true, // 自动吸附连接线 allowBlank: false, // 不允许只有一个节点的线 // allowMulti: false, // 不允许在两个节点间创建多个线 highlight: true, // 高亮可被连接的点 }, grid: true, history: true, snapline: { enabled: true, // 对齐线 sharp: true, }, scroller: { enabled: true, pageVisible: false, pageBreak: false, pannable: true, }, // mousewheel: { // enabled: true, // modifiers: ['ctrl', 'meta'], // }, }); this.dnd = new Dnd({ target: this.graph, scaled: false, animation: true, }); this.graph.on('edge:connected', ({ isNew, edge }) => { if (isNew) { that.handleCellClick(edge); } }); this.graph.on('cell:mouseenter', ({ cell }) => { if (this.clickCellMap[cell.id]) { clearTimeout(this.clickCellMap[cell.id]); delete this.clickCellMap[cell.id]; } if (cell.isNode()) { let offset = { x: 10, y: 10 }; if (cell.shape === 'polygon') { offset = { x: 20, y: 20 }; } cell.addTools([ { name: 'boundary', args: { attrs: { fill: '#7c68fc', stroke: '#333', 'stroke-width': 1, 'fill-opacity': 0.2, }, }, }, { name: 'button-remove', args: { x: 0, y: 0, offset, }, }, ]); } else { cell.addTools(['button-remove']); } }); this.graph.on('cell:mouseleave', ({ cell }) => { cell.removeTools(); }); this.graph.on('cell:click', ({ cell }) => { that.handleCellClick(cell); }); this.graph.on('node:added', ({ cell }) => { that.handleCellClick(cell); }); if (this.cells.length > 0) { this.graph.fromJSON({ cells: this.cells }); this.$nextTick(() => { this.graph.centerContent(); }); this.cells = []; } }, async saveFlow() { let workflowValid; await this.$refs.workflow.validate((valid) => { workflowValid = valid; }); if (!workflowValid) { this.$message({ type: 'warning', message: '请完成流程信息的配置', }); return; } const data = this.graph.toJSON(); const { cells } = data; const workflow = { name: this.workflow.name, product_line: this.workflow.productLine, description: this.workflow.description, created: this.workflow.created, icon: this.workflow.icon, iconColor: this.workflow.iconColor, created_by: this.workflow.created_by, tpl: deepCopy(this.workflow.tpl), }; const flowchart = {}; flowchart.workflow = deepCopy(workflow); flowchart.nodes = []; flowchart.edges = []; const that = this; const cellValid = cells.every((cell) => { // 线 if (cell.shape === 'edge') { const { id } = cell; if (!that.workflow.edgeConfigs[id]) { const cellObj = that.graph.getCell(id); that.handleCellClick(cellObj); that.$nextTick(() => { that.$refs.edge.validate(); }); return false; } const temp = deepCopy(cell); temp.valueData = that.workflow.edgeConfigs[id]; flowchart.edges.push(temp); return true; } const { id } = cell; if (!that.workflow.nodeConfigs[id]) { const cellObj = that.graph.getCell(id); that.activeTab = 'cellInfo'; that.handleCellClick(cellObj); that.$nextTick(() => { that.$refs.node.validate(); }); return false; } const temp = deepCopy(cell); temp.valueData = that.workflow.nodeConfigs[id]; flowchart.nodes.push(temp); return true; }); // workflow.flowchart = flowchart; if (!cellValid) { return; } this.internalChange = true; this.$emit('update', flowchart); this.$nextTick(() => { this.internalChange = false; }); }, }, }; </script> <style scoped> .process-edit-container { margin-top: 20px; height: calc(100% - 20px); } .process-canvas { width: 100%; height: 100%; } .process-component-list { height: 100%; background-color: #e6e6e6; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-around; align-content: flex-start; } .father { height: calc(100%); } .process-box { width: calc(100% - 15px); height: 280px; overflow-y: auto; margin-left: 10px; } .export-box { height: 200px; margin-left: 10px; width: calc(100% - 15px); } </style>
下面是将上面的代码作为一个公共组件使用,通过props传入可进行编辑操作
<template> <div> <span class="staff-title">{{ activeTitle }}</span> <process-edit :data="processInfo" :label-type="labelType" @update="handleSave" /> </div> </template> <script> //import { getRegisterTestProcess, postRegisterTestProcess } from '@/api/integration_test'; import { deepCopy } from '../../utils/util'; import ProcessEdit from './components/ProcessEdit.vue'; export default { name: 'IntegrationTestProcess', components: { ProcessEdit, }, data: () => ({ init: false, realId: null, processInfo: { name: '', projectId: null, description: '', icon: 'el-icon-user', iconColor: 'blue', tpl: [], flow_chart: { nodes: [], edges: [], workflow: {}, }, }, activeTitle: '', processId: -1, labelType: 1, }), watch: { processInfo: { handler(val) { if (this.init) { return; } const data = deepCopy(val); const statusList = []; const transformList = []; const statusIDList = []; const transformIDList = []; // eslint-disable-next-line no-restricted-syntax for (const arrayValue of data.nodes) { if (statusIDList.indexOf(arrayValue.valueData.id) === -1) { statusIDList.push(arrayValue.valueData.id); } else { delete arrayValue.valueData.id; } arrayValue.valueData.tmpId = arrayValue.id; statusList.push(arrayValue.valueData); arrayValue.label = arrayValue.valueData.name; } // eslint-disable-next-line no-restricted-syntax for (const transformValue of data.edges) { if (transformIDList.indexOf(transformValue.valueData.id) === -1) { transformIDList.push(transformValue.valueData.id); } else { delete transformValue.valueData.id; } transformValue.valueData.tmpId = transformValue.id; transformList.push(transformValue.valueData); } const workflowValue = Object.assign({}, data.workflow); workflowValue.flowchart = data; // const jsonData = { // workflow: workflowValue, // status: statusList, // transform: transformList, // }; }, }, }, created() { this.processId = this.$route.params.process_id; if (Number(this.processId) !== -1 && this.processId) { this.init = true; this.activeTitle = '编辑流程'; this.realId = this.processId; this.labelType = 2; getRegisterTestProcess(this.processId).then((res) => { this.processInfo = res; this.$nextTick(() => { this.init = false; }); }, (err) => { this.$message({ type: 'error', message: err.errmsg, }); console.error(err); }); } else { this.activeTitle = '新建流程'; } }, methods: { handleSave(val) { const data = deepCopy(val); const statusList = []; const transformList = []; const statusIDList = []; const transformIDList = []; // eslint-disable-next-line no-restricted-syntax for (const arrayValue of data.nodes) { if (statusIDList.indexOf(arrayValue.valueData.id) === -1) { statusIDList.push(arrayValue.valueData.id); } else { delete arrayValue.valueData.id; } arrayValue.valueData.tmpId = arrayValue.id; statusList.push(arrayValue.valueData); arrayValue.label = arrayValue.valueData.name; } // eslint-disable-next-line no-restricted-syntax for (const transformValue of data.edges) { if (transformIDList.indexOf(transformValue.valueData.id) === -1) { transformIDList.push(transformValue.valueData.id); } else { delete transformValue.valueData.id; } transformValue.valueData.tmpId = transformValue.id; transformList.push(transformValue.valueData); } const workflowValue = Object.assign({}, data.workflow); workflowValue.flowchart = data; const jsonData = { workflow: workflowValue, status: statusList, transform: transformList, }; const flows = []; jsonData.transform.forEach((item) => { let obj = {}; jsonData.status.forEach((key) => { if (key.tmpId === item.sourceStatus) { obj = { from: key.name, }; } }); jsonData.status.forEach((key) => { if (item.destinationStatus === key.tmpId) { obj.to = key.name; } }); flows.push(obj); }); const nodesList = jsonData.status.map((item) => { const importParam = {}; item.paramList.forEach((key) => { if (key.param === 0 || key.param === 1) { importParam[key.name] = { type: key.param, value: key.defaultValue, description: key.description, }; } else if (key.param === 2) { const nodeObj = jsonData.status.find(item => item.tmpId === key.nodeName); importParam[key.name] = { type: key.param, value: nodeObj && nodeObj.name ? `${nodeObj.name}.${key.export}` : '', description: key.description, }; } else if (key.param === 3) { importParam[key.name] = { type: key.param, value: key.defaultValue, description: key.description, }; } }); const obj = { name: item.name, description: item.description, type: item.type, component_id: item.component_id, import_param: importParam, export_param: item.exportParam, }; return obj; }); const nodeList = nodesList.filter(item => item.name !== '开始' && item.name !== '结束'); const flowData = { name: jsonData.workflow.name, description: jsonData.workflow.description, product_line: jsonData.workflow.product_line, created_by: jsonData.workflow.created_by, }; const resultData = { ...flowData, nodes: nodeList, flows, flow_chart: jsonData.workflow.flowchart, }; if (this.realId === null) { postRegisterTestProcess(resultData).then((res) => { if (res) { this.$router.push({ name: 'IntegrationTestJobList' }); this.$message.success('流程新建成功!'); } }, (err) => { this.$message({ type: 'error', message: err.errmsg, }); console.error(err); }); } else { const jsonData = { process_id: this.realId, ...resultData, }; postRegisterTestProcess(jsonData).then((res) => { if (res) { this.$router.push({ name: 'IntegrationTestJobList' }); this.$message.success('流程修改成功!'); } }, (err) => { this.$message({ type: 'error', message: err.errmsg, }); console.error(err); }); } }, }, }; </script> <style scoped> .staff-title { display: block; width: 100%; height: 50px; padding: 13px 0 5px 30px; font-size: 16px; margin-top: 20px; border-bottom: 1px solid #ccc; margin-bottom: 30px; background-color: skyblue; } </style>
// 深拷贝 export function deepCopy(obj, cache = []) { // just return if obj is immutable value if (obj === null || typeof obj !== 'object') { return obj } // if obj is hit, it is in circular structure const hit = find(cache, (c) => c.original === obj) if (hit) { return hit.copy } const copy = Array.isArray(obj) ? [] : {} // put the copy into cache at first // because we want to refer it in recursive deepCopy cache.push({ original: obj, copy }) Object.keys(obj).forEach((key) => { copy[key] = deepCopy(obj[key], cache) }) return copy }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。