|
@@ -0,0 +1,839 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <el-row style="margin-left: 0px; margin-bottom: 20px; margin-right: 25px">
|
|
|
+ <el-col :span="1">
|
|
|
+ <el-button type="primary" @click="back">返回</el-button>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="1" :offset="22">
|
|
|
+ <el-button type="success" :loading="saveLoading" @click="saveWorkflow">保存</el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row>
|
|
|
+ <el-form ref="form" :model="workflowInfo" label-width="100px" style="width: 100%">
|
|
|
+ <el-form-item label="工作流名称">
|
|
|
+ <el-input v-model="workflowInfo.wfName" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="工作流描述">
|
|
|
+ <el-input v-model="workflowInfo.wfDescription" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="定时信息">
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-select v-model="workflowInfo.timeExpressionType" placeholder="时间表达式类型">
|
|
|
+ <el-option
|
|
|
+ v-for="item in timeExpressionTypeOptions"
|
|
|
+ :key="item.key"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.key"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-input
|
|
|
+ v-model="workflowInfo.timeExpression"
|
|
|
+ placeholder="CRON 填写 CRON 表达式,API 无需填写"
|
|
|
+ />
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <el-button type="text" @click="onClickValidateTimeExpression">校验定时参数</el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="生命周期">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="workflowInfo.lifeCycle"
|
|
|
+ type="datetimerange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始时间"
|
|
|
+ end-placeholder="结束时间"
|
|
|
+ format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ date-format="YYYY-MM-DD"
|
|
|
+ time-format="hh:mm:ss"
|
|
|
+ @change="changedatepicker"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="最大实例数">
|
|
|
+ <el-input-number v-model="workflowInfo.maxWfInstanceNum" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="报警配置">
|
|
|
+ <el-select
|
|
|
+ v-model="workflowInfo.notifyUserIds"
|
|
|
+ multiple
|
|
|
+ filterable
|
|
|
+ placeholder="选择报警通知人员"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="user in userList"
|
|
|
+ :key="user.id"
|
|
|
+ :label="user.username"
|
|
|
+ :value="user.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row>
|
|
|
+ <div class="power-flow">
|
|
|
+ <div class="power-dag" id="fullInc">
|
|
|
+ <!--
|
|
|
+ -->
|
|
|
+ <PowerWorkflow
|
|
|
+ :onClickImportSpecialNode="onClickImportSpecialNode"
|
|
|
+ :onClickImportNode="onClickImportNode"
|
|
|
+ :selectNode="selectNode"
|
|
|
+ :nodes="peworkflowDAG.nodes"
|
|
|
+ :edges="peworkflowDAG.edges"
|
|
|
+ @on-selected-node="handleSelectedNode"
|
|
|
+ @get-dag="getDagsv"
|
|
|
+ :defaultWidthInc="234"
|
|
|
+ fullInc="fullInc"
|
|
|
+ >
|
|
|
+ <div class="job-panl" v-if="selectNode !== null">
|
|
|
+ <el-form ref="form" :model="nodeInfo">
|
|
|
+ <el-form-item label="任务名称" v-if="nodeInfo.type != '2'">
|
|
|
+ <!-- :style="{ width: 'calc(100% - 90px)' }" -->
|
|
|
+ <el-select
|
|
|
+ v-model="nodeInfo.jobId"
|
|
|
+ filterable
|
|
|
+ remote
|
|
|
+ reserve-keyword
|
|
|
+ placeholder="请输入关键词"
|
|
|
+ :remote-method="remoteTaskData"
|
|
|
+ :loading="taskLoading"
|
|
|
+ style="width: 410px"
|
|
|
+ @focus="handleWaitFocus"
|
|
|
+ @change="handleWaitTaskChange"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in waitTaskList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.jobName || item.wfName"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="节点名称">
|
|
|
+ <el-input
|
|
|
+ v-model="nodeInfo.nodeName"
|
|
|
+ @input="handleNodeName"
|
|
|
+ :style="{ width: 'calc(100% - 90px)' }"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="节点参数" v-if="nodeInfo.type != '2'">
|
|
|
+ <el-input v-model="nodeInfo.nodeParams" :style="{ width: 'calc(100% - 90px)' }" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="是否启用" v-if="nodeInfo.type != '2'">
|
|
|
+ <el-switch v-model="nodeInfo.enable" />
|
|
|
+ <img
|
|
|
+ class="job-panl-icon"
|
|
|
+ v-if="nodeInfo.enable"
|
|
|
+ src="@/assets/powerJob/start.svg"
|
|
|
+ height="18"
|
|
|
+ width="18"
|
|
|
+ alt
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="失败跳过" v-if="nodeInfo.type != '2'">
|
|
|
+ <el-switch v-model="nodeInfo.skipWhenFailed" />
|
|
|
+ <img
|
|
|
+ class="job-panl-icon"
|
|
|
+ v-if="nodeInfo.skipWhenFailed"
|
|
|
+ src="../../assets/skip.svg"
|
|
|
+ height="18"
|
|
|
+ width="18"
|
|
|
+ alt
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div v-if="nodeInfo.type == '2'" class="judge-message-params">
|
|
|
+ <p>节点参数</p>
|
|
|
+ <!-- @onCodeChange="onCodeChange" -->
|
|
|
+ <JSEditor
|
|
|
+ :code="nodeInfo.nodeParams"
|
|
|
+ @on-code-change="onCodeChange"
|
|
|
+ key="nodeParams"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="job-panl-btn">
|
|
|
+ <el-button type="success" @click="handleNodeSave">保存</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </PowerWorkflow>
|
|
|
+ <el-drawer
|
|
|
+ title="请选择需要导入工作流的任务"
|
|
|
+ v-model="importDrawerVisible"
|
|
|
+ direction="rtl"
|
|
|
+ size="60%"
|
|
|
+ >
|
|
|
+ <div class="power-import-body">
|
|
|
+ <el-row>
|
|
|
+ <el-form :inline="true" :model="jobQueryContent" class="el-form--inline">
|
|
|
+ <el-form-item label="任务 ID">
|
|
|
+ <el-input v-model="jobQueryContent.jobId" placeholder="任务 ID" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="关键字">
|
|
|
+ <el-input v-model="jobQueryContent.keyword" placeholder="关键字" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="listJobInfos">查询</el-button>
|
|
|
+ <el-button type="cancel" @click="onClickReset">重置</el-button>
|
|
|
+ <el-button type="cancel" @click="onBulkImport">批量导入</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-row>
|
|
|
+ <el-table
|
|
|
+ class="power-import-table"
|
|
|
+ :data="jobInfoPageResult.data"
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
+ >
|
|
|
+ <el-table-column type="selection" width="55" />
|
|
|
+ <el-table-column property="id" label="任务 ID" />
|
|
|
+ <el-table-column property="jobName" label="任务名称" />
|
|
|
+ <el-table-column label="操作">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button size="medium" @click="importTask([scope.row])">导入</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-row>
|
|
|
+ <el-pagination
|
|
|
+ layout="prev, pager, next"
|
|
|
+ :total="jobInfoPageResult.totalItems"
|
|
|
+ :page-size="jobInfoPageResult.pageSize"
|
|
|
+ @current-change="onClickChangePage"
|
|
|
+ />
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-row>
|
|
|
+ <el-dialog v-model="timeExpressionValidatorVisible" v-if="timeExpressionValidatorVisible">
|
|
|
+ <TimeExpressionValidator
|
|
|
+ :time-expression="workflowInfo.timeExpression"
|
|
|
+ :time-expression-type="workflowInfo.timeExpressionType"
|
|
|
+ />
|
|
|
+ </el-dialog>
|
|
|
+ <el-drawer title="工作流节点引入" v-model="workflowVisible" direction="rtl" size="60%">
|
|
|
+ <workflow-manager :isWorkflow="true" @on-import-node="onImportChildWorkflowNode" />
|
|
|
+ </el-drawer>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { onMounted, ref, watch, defineComponent } from 'vue'
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import JSEditor from './JSEditor.vue'
|
|
|
+import TimeExpressionValidator from './TimeExpressionValidator.vue'
|
|
|
+import PowerWorkflow from './PowerWorkflow.vue'
|
|
|
+import WorkflowManager from './WorkflowManager.vue'
|
|
|
+import { getjobList, getTasktableList } from '@/api/model/taskManagement'
|
|
|
+import { getuserMessage } from '@/api/model/workflowManagement'
|
|
|
+import startsvg from '@/assets/powerJob/start.svg'
|
|
|
+import skipsvg from '@/assets/powerJob/skip.svg'
|
|
|
+import * as common from '@/utils/common'
|
|
|
+import {
|
|
|
+ getworkflowfetch,
|
|
|
+ getworkflowsave,
|
|
|
+ getworkflowsaveNode,
|
|
|
+ getworkflowtableList
|
|
|
+} from '@/api/model/workflowManagement'
|
|
|
+// import { concat } from 'node_modules/_@types_lodash@4.17.1@@types/lodash'
|
|
|
+const powerappId = window.localStorage.getItem('powerAppId')
|
|
|
+const { push } = useRouter() // 路由跳转
|
|
|
+const workflowInfo = ref({
|
|
|
+ id: '',
|
|
|
+ appId: powerappId,
|
|
|
+ enable: true,
|
|
|
+ maxWfInstanceNum: 1,
|
|
|
+ notifyUserIds: [],
|
|
|
+ timeExpression: undefined,
|
|
|
+ timeExpressionType: undefined,
|
|
|
+ wfDescription: undefined,
|
|
|
+ wfName: undefined,
|
|
|
+ lifeCycle: []
|
|
|
+})
|
|
|
+const nodeInfoChange = (icon, index) => {
|
|
|
+ return function (value) {
|
|
|
+ if (!this.selectNode) return
|
|
|
+ const group = this.selectNode.getContainer()
|
|
|
+ const current = group.getChildByIndex(index)
|
|
|
+ if (value) {
|
|
|
+ current.attr({ img: icon })
|
|
|
+ } else {
|
|
|
+ current.attr({ img: '' })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 节点类型映射 */
|
|
|
+const nodeType = ref({
|
|
|
+ 1: (item) => {
|
|
|
+ return {
|
|
|
+ type: 'flow-node',
|
|
|
+ size: [240, 70],
|
|
|
+ leftText: item.jobId,
|
|
|
+ titleText: item.nodeName,
|
|
|
+ icon1: item.enable ? startsvg : '',
|
|
|
+ icon2: item.skipWhenFailed ? skipsvg : ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 2: (item) => {
|
|
|
+ return {
|
|
|
+ type: 'max-diamond-node',
|
|
|
+ text: !item.nodeName ? '判断' : item.nodeName,
|
|
|
+ style: {
|
|
|
+ sideLength: 80,
|
|
|
+ textStyle: {
|
|
|
+ // fill: "#FFFFFF",
|
|
|
+ }
|
|
|
+ // fill: "#FE9201",
|
|
|
+ // stroke: "#D45547",
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 3: (item) => {
|
|
|
+ return {
|
|
|
+ type: 'flow-child-node',
|
|
|
+ size: [240, 70],
|
|
|
+ leftText: item.jobId,
|
|
|
+ titleText: item.nodeName,
|
|
|
+ icon1: item.enable ? startsvg : '',
|
|
|
+ icon2: item.skipWhenFailed ? skipsvg : ''
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+const nodeInfo = ref({
|
|
|
+ id: null,
|
|
|
+ jobId: null,
|
|
|
+ nodeName: '',
|
|
|
+ nodeParams: '',
|
|
|
+ enable: true,
|
|
|
+ skipWhenFailed: true
|
|
|
+})
|
|
|
+const timeExpressionTypeOptions = ref([
|
|
|
+ { key: 'API', label: 'API' },
|
|
|
+ { key: 'CRON', label: 'CRON' }
|
|
|
+])
|
|
|
+const userList = ref([])
|
|
|
+
|
|
|
+// 导入任务相关
|
|
|
+const importDrawerVisible = ref(false)
|
|
|
+const jobQueryContent = ref({
|
|
|
+ appId: powerappId,
|
|
|
+ index: 0,
|
|
|
+ pageSize: 8,
|
|
|
+ jobId: undefined,
|
|
|
+ keyword: undefined
|
|
|
+})
|
|
|
+const jobInfoPageResult = ref({
|
|
|
+ pageSize: 20,
|
|
|
+ totalItems: 0,
|
|
|
+ data: []
|
|
|
+})
|
|
|
+
|
|
|
+// 事件(1:新增起点,2:新增终点,3:删除节点;4:删除边)
|
|
|
+const event = ref(undefined)
|
|
|
+const from = ref(undefined)
|
|
|
+
|
|
|
+// 时间表达式校验窗口
|
|
|
+const timeExpressionValidatorVisible = ref(false)
|
|
|
+/** DAG信息 */
|
|
|
+const peworkflowDAG = ref({
|
|
|
+ nodes: [],
|
|
|
+ edges: []
|
|
|
+})
|
|
|
+/** 保存按钮loading */
|
|
|
+const saveLoading = ref(false)
|
|
|
+/** 流程图实例 */
|
|
|
+const powerFlow = ref(null)
|
|
|
+/** 选中的数据 */
|
|
|
+const multipleSelection = ref([])
|
|
|
+/** 当前的节点信息 */
|
|
|
+const taskList = ref([])
|
|
|
+/** 当前选中的节点 */
|
|
|
+const selectNode = ref(null)
|
|
|
+/** 重置节点方法 */
|
|
|
+const resetNodes = ref(null)
|
|
|
+/** 待选任务列表 */
|
|
|
+const waitTaskList = ref([])
|
|
|
+/** 任务搜索loading */
|
|
|
+const taskLoading = ref(false)
|
|
|
+/** 任务节流 */
|
|
|
+const taskTimeout = ref(null)
|
|
|
+/** 工作流引入显隐控制 */
|
|
|
+const workflowVisible = ref(false)
|
|
|
+const route = useRouter()
|
|
|
+// 返回上一页
|
|
|
+const back = () => {
|
|
|
+ route.back()
|
|
|
+}
|
|
|
+// 点击重置按钮
|
|
|
+const onClickReset = () => {
|
|
|
+ jobQueryContent.value.keyword = undefined
|
|
|
+ jobQueryContent.value.jobId = undefined
|
|
|
+ listJobInfos()
|
|
|
+}
|
|
|
+// 列出符合当前搜索条件的任务
|
|
|
+const listJobInfos = async () => {
|
|
|
+ const res = await getjobList(jobQueryContent.value)
|
|
|
+ jobInfoPageResult.value = res.data
|
|
|
+}
|
|
|
+// 点击 换页
|
|
|
+const onClickChangePage = (index) => {
|
|
|
+ // 后端从0开始,前端从1开始
|
|
|
+ jobQueryContent.value.index = index - 1
|
|
|
+ listJobInfos()
|
|
|
+}
|
|
|
+const onClickImportNode = () => {
|
|
|
+ listJobInfos()
|
|
|
+ importDrawerVisible.value = true
|
|
|
+}
|
|
|
+/** 引入其他类型节点, 判断,工作流 */
|
|
|
+const onClickImportSpecialNode = (data) => {
|
|
|
+ const { type } = data
|
|
|
+ if (type === 3) {
|
|
|
+ workflowVisible.value = true
|
|
|
+ } else {
|
|
|
+ importTask([
|
|
|
+ {
|
|
|
+ appId: workflowInfo.value.appId,
|
|
|
+ jobParams: 'true',
|
|
|
+ type: type,
|
|
|
+ workflowId: workflowInfo.value.id,
|
|
|
+ jobName: ''
|
|
|
+ }
|
|
|
+ ])
|
|
|
+ }
|
|
|
+}
|
|
|
+/** 引入嵌套工作流节点 */
|
|
|
+const onImportChildWorkflowNode = (data) => {
|
|
|
+ importTask([
|
|
|
+ {
|
|
|
+ appId: workflowInfo.value.appId,
|
|
|
+ nodeName: data.wfName,
|
|
|
+ enable: data.enable,
|
|
|
+ id: data.id,
|
|
|
+ nodeParams: '',
|
|
|
+ type: 3,
|
|
|
+ workflowId: workflowInfo.value.id,
|
|
|
+ jobName: data.wfName
|
|
|
+ }
|
|
|
+ ])
|
|
|
+}
|
|
|
+const onClickValidateTimeExpression = () => {
|
|
|
+ timeExpressionValidatorVisible.value = true
|
|
|
+}
|
|
|
+/** 选中节点 */
|
|
|
+const handleSelectedNode = (item) => {
|
|
|
+ selectNode.value = item
|
|
|
+
|
|
|
+ // 从节点列表找到节点
|
|
|
+ let index = getNodeIndexById(item.get('model').nodeId)
|
|
|
+
|
|
|
+ let node = taskList.value[index]
|
|
|
+ // if (node.type === "condition") return false;
|
|
|
+ remoteTaskData(null, node.jobId)
|
|
|
+ nodeInfo.value = {
|
|
|
+ type: node.nodeType,
|
|
|
+ jobId: node.jobId,
|
|
|
+ nodeName: node.nodeName ? node.nodeName : node.nodeType == 2 ? '判断' : node.nodeName,
|
|
|
+ nodeParams: node.nodeParams,
|
|
|
+ enable: node.enable,
|
|
|
+ skipWhenFailed: node.skipWhenFailed,
|
|
|
+ id: item.get('model').nodeId || item.get('model').id
|
|
|
+ }
|
|
|
+}
|
|
|
+/** 多选节点 */
|
|
|
+const handleSelectionChange = (val) => {
|
|
|
+ multipleSelection.value = val
|
|
|
+}
|
|
|
+/** 修改节点名称 */
|
|
|
+const handleNodeName = (value) => {
|
|
|
+ const nodeItem = powerFlow.value.graph.get('selectedItem')
|
|
|
+ const group = nodeItem.getContainer()
|
|
|
+ const current = group.getChildByIndex(2)
|
|
|
+ current.attr('text', value)
|
|
|
+}
|
|
|
+/** 获取工作流程图实例 */
|
|
|
+const getDagsv = (powerFlowda, { resetNodesdata }) => {
|
|
|
+ powerFlow.value = powerFlowda
|
|
|
+ resetNodes.value = resetNodesdata
|
|
|
+}
|
|
|
+/** 根据nodeId找任务节点索引 */
|
|
|
+const getNodeIndexById = (nodeId) => {
|
|
|
+ return taskList.value.findIndex((item) => item.nodeId == nodeId)
|
|
|
+}
|
|
|
+/** 获取工作流信息 */
|
|
|
+const getWorkflowInfo = async (fit) => {
|
|
|
+ let params = {
|
|
|
+ workflowId: workflowInfo.value.id,
|
|
|
+ appId: workflowInfo.value.appId
|
|
|
+ }
|
|
|
+ const res = await getworkflowfetch(params)
|
|
|
+ // workflowInfo.value = { ...workflowInfo.value, ...res.data }
|
|
|
+ if (res.data.peworkflowDAG) {
|
|
|
+ taskList.value = res.data.peworkflowDAG.nodes
|
|
|
+ peworkflowDAG.value = res.data.peworkflowDAG
|
|
|
+ nextTick(() => {
|
|
|
+ resetNodes.value
|
|
|
+ if (fit) {
|
|
|
+ // this.powerFlow.graph.fitView(20);
|
|
|
+ // 改为layout适配会对节点少的时候友好一点
|
|
|
+ powerFlow.value.graph.layout()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+/** 保存工作流全局信息 */
|
|
|
+const saveWorkflow = async () => {
|
|
|
+ // 改为不需要dag信息
|
|
|
+ const flowData = powerFlow.value.graph.save()
|
|
|
+ console.log(flowData)
|
|
|
+ let dagInfo = {
|
|
|
+ nodes: flowData.nodes.map((item) => ({ nodeId: item.id })),
|
|
|
+ edges: flowData.edges.map((item) => {
|
|
|
+ const property = {}
|
|
|
+ if (item.label) {
|
|
|
+ property.property = item.label === 'Y' ? 'true' : 'false'
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ from: item.source,
|
|
|
+ to: item.target,
|
|
|
+ ...property
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ let params = {
|
|
|
+ ...workflowInfo.value,
|
|
|
+ dag: dagInfo
|
|
|
+ }
|
|
|
+ const lifeCycle = workflowInfo.value.lifeCycle
|
|
|
+ if (lifeCycle && Array.isArray(lifeCycle)) {
|
|
|
+ const start = new Date(lifeCycle[0]).getTime()
|
|
|
+ const end = new Date(lifeCycle[1]).getTime()
|
|
|
+ params.lifeCycle = {
|
|
|
+ start,
|
|
|
+ end
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const res = await getworkflowsave(params)
|
|
|
+ ElMessage.success('成功')
|
|
|
+ if (!workflowInfo.value.id) workflowInfo.value.id = res
|
|
|
+}
|
|
|
+/** 导入任务节点数据 */
|
|
|
+const importTask = async (taskListArr) => {
|
|
|
+ if (taskListArr.length === 0) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let type = ''
|
|
|
+
|
|
|
+ let data = taskListArr.map((item) => {
|
|
|
+ type = item.type ? Number(item.type) : 1
|
|
|
+ return {
|
|
|
+ appId: item.appId,
|
|
|
+ enable: item.enable,
|
|
|
+ skipWhenFailed: item.skipWhenFailed,
|
|
|
+ nodeName: item.jobName,
|
|
|
+ jobId: item.id,
|
|
|
+ nodeParams: item.jobParams,
|
|
|
+ workflowId: workflowInfo.value.id,
|
|
|
+ // type: "JOB"
|
|
|
+ type: type
|
|
|
+ }
|
|
|
+ })
|
|
|
+ let res = await getworkflowsaveNode(data)
|
|
|
+ console.log(res)
|
|
|
+ // 先移动视口一个节点点的位置
|
|
|
+ // 获取缩放比例
|
|
|
+ const zoom = powerFlow.value.graph.getZoom()
|
|
|
+ powerFlow.value.graph.translate(260 * zoom, 0)
|
|
|
+ const viewPointEnd = powerFlow.value.graph.getPointByCanvas(0, 0)
|
|
|
+ res.data.forEach((item, index) => {
|
|
|
+ const nodeText = nodeType.value[type](item)
|
|
|
+ powerFlow.value.graph.add('node', {
|
|
|
+ ...item,
|
|
|
+
|
|
|
+ id: `${item.id}`,
|
|
|
+ nodeId: `${item.id}`,
|
|
|
+ nodeType: `${item.type}`,
|
|
|
+ // type: nodeType[item.type],
|
|
|
+ size: [240, 70],
|
|
|
+ x: viewPointEnd.x + 20,
|
|
|
+ y: viewPointEnd.y + 70 * index + 20 + index * 10,
|
|
|
+ ...nodeText
|
|
|
+ // leftText: item.jobId,
|
|
|
+ // titleText: item.nodeName,
|
|
|
+ // icon1: item.enable ? require("../../assets/start.svg") : "",
|
|
|
+ // icon2: item.skipWhenFailed ? require("../../assets/skip.svg") : "",
|
|
|
+ })
|
|
|
+ })
|
|
|
+ let resData = res.data.map((item) => ({
|
|
|
+ ...item,
|
|
|
+ nodeType: item.type,
|
|
|
+ nodeParams: item.nodeParams,
|
|
|
+ nodeId: item.id
|
|
|
+ }))
|
|
|
+ let taskListAr = resData.concat(...taskList.value)
|
|
|
+ taskList.value = taskListAr
|
|
|
+}
|
|
|
+/** 保存单个节点 */
|
|
|
+const handleNodeSave = async (value = {}) => {
|
|
|
+ let data = [
|
|
|
+ {
|
|
|
+ ...nodeInfo.value,
|
|
|
+ appId: workflowInfo.value.appId,
|
|
|
+ workflowId: workflowInfo.value.id,
|
|
|
+ ...value
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ await getworkflowsaveNode(data)
|
|
|
+
|
|
|
+ let index = getNodeIndexById(nodeInfo.value.id)
|
|
|
+
|
|
|
+ taskList.value[index] = {
|
|
|
+ ...taskList.value[index],
|
|
|
+ nodeName: nodeInfo.value.nodeName,
|
|
|
+ nodeParams: nodeInfo.value.nodeParams,
|
|
|
+ enable: nodeInfo.value.enable,
|
|
|
+ skipWhenFailed: nodeInfo.value.skipWhenFailed
|
|
|
+ }
|
|
|
+
|
|
|
+ ElMessage.success('成功')
|
|
|
+}
|
|
|
+/** 批量导入工作流 */
|
|
|
+const onBulkImport = async () => {
|
|
|
+ if (multipleSelection.value.length === 0) {
|
|
|
+ ElMessage.warning('请至少选中一条数据')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ await importTask(multipleSelection.value)
|
|
|
+}
|
|
|
+/** 远程加载任务列表数据 */
|
|
|
+const remoteTaskData = async (value, jobId) => {
|
|
|
+ // clearTimeout(taskTimeout.value)
|
|
|
+ // taskTimeout.value = setTimeout(async () => {
|
|
|
+ // taskLoading.value = true
|
|
|
+ // const res = null
|
|
|
+ // let params = {
|
|
|
+ // ...this.jobQueryContent,
|
|
|
+ // index: 0,
|
|
|
+ // keyword: value,
|
|
|
+ // jobId: jobId
|
|
|
+ // }
|
|
|
+ // if (this.nodeInfo.type === 3) {
|
|
|
+ // res = await getworkflowtableList(params)
|
|
|
+ // } else {
|
|
|
+ // res = await getTasktableList(params)
|
|
|
+ // }
|
|
|
+ // waitTaskList.value = res.data
|
|
|
+ // taskLoading.value = false
|
|
|
+ // }, 100)
|
|
|
+ clearTimeout(taskTimeout.value)
|
|
|
+ taskTimeout.value = setTimeout(async () => {
|
|
|
+ taskLoading.value = true
|
|
|
+ if (nodeInfo.value.type === 3) {
|
|
|
+ const res = await getworkflowtableList({
|
|
|
+ ...jobQueryContent.value,
|
|
|
+ index: 0,
|
|
|
+ keyword: value,
|
|
|
+ jobId: jobId
|
|
|
+ })
|
|
|
+ waitTaskList.value = res.data.data
|
|
|
+ } else {
|
|
|
+ const res = await getTasktableList({
|
|
|
+ ...jobQueryContent.value,
|
|
|
+ index: 0,
|
|
|
+ keyword: value,
|
|
|
+ jobId: jobId
|
|
|
+ })
|
|
|
+ waitTaskList.value = res.data.data
|
|
|
+ }
|
|
|
+ taskLoading.value = false
|
|
|
+ })
|
|
|
+}
|
|
|
+/** 选中任务时 */
|
|
|
+const handleWaitTaskChange = (value) => {
|
|
|
+ // 找到节点信息
|
|
|
+ let current = waitTaskList.value.find((item) => item.id === value)
|
|
|
+
|
|
|
+ let currentShape = selectNode.value.getContainer().getChildByIndex(1)
|
|
|
+
|
|
|
+ currentShape.attr({ text: current.id })
|
|
|
+ let index = getNodeIndexById(selectNode.value.get('model').nodeId)
|
|
|
+ powerFlow.value.graph.updateItem(selectNode.value, {
|
|
|
+ leftText: current.id,
|
|
|
+ jobId: current.id
|
|
|
+ })
|
|
|
+ taskList.value[index] = {
|
|
|
+ ...taskList.value[index],
|
|
|
+ jobId: current.id
|
|
|
+ }
|
|
|
+ nodeInfo.value.jobId = value
|
|
|
+}
|
|
|
+/** 节点外点击时单独处理 */
|
|
|
+const handleWaitFocus = () => {
|
|
|
+ powerFlow.value.graph.set('noKeyDown', true)
|
|
|
+}
|
|
|
+/** 判断节点参数改变 */
|
|
|
+const onCodeChange = (code) => {
|
|
|
+ nodeInfo.value.nodeParams = code
|
|
|
+}
|
|
|
+const changedatepicker = (value) => {
|
|
|
+ console.log('datepicker====>>>>', value)
|
|
|
+}
|
|
|
+onMounted(async () => {
|
|
|
+ const res = await getuserMessage()
|
|
|
+ userList.value = res.data
|
|
|
+ // 读取传递数据,如果是修改,需要先将数据绘制上去
|
|
|
+ let modify = route.currentRoute.value.query.modify
|
|
|
+ if (modify !== 'false') {
|
|
|
+ let queryData = JSON.parse(decodeURIComponent(route.currentRoute.value.query.workflowInfo))
|
|
|
+ if (queryData.lifeCycle) {
|
|
|
+ const { start, end } = queryData.lifeCycle
|
|
|
+ // queryData.lifeCycle = [common.getDateTime(new Date(start)), common.getDateTime(new Date(end))]
|
|
|
+ queryData.lifeCycle = [new Date(start), new Date(end)]
|
|
|
+ } else {
|
|
|
+ queryData.lifeCycle = null
|
|
|
+ }
|
|
|
+ workflowInfo.value = queryData
|
|
|
+ // workflowInfo.value = JSON.parse(decodeURIComponent(route.currentRoute.value.query.workflowInfo))
|
|
|
+ // if (workflowInfo.value.lifeCycle) {
|
|
|
+ // const { start, end } = workflowInfo.value.lifeCycle
|
|
|
+ // workflowInfo.value.lifeCycle = [
|
|
|
+ // common.getDateTime(new Date(start)),
|
|
|
+ // common.getDateTime(new Date(end))
|
|
|
+ // ]
|
|
|
+ // } else {
|
|
|
+ // workflowInfo.value.lifeCycle = null
|
|
|
+ // }
|
|
|
+ workflowInfo.value.appId = powerappId
|
|
|
+ getWorkflowInfo(true)
|
|
|
+ }
|
|
|
+})
|
|
|
+watch(
|
|
|
+ () => 'nodeInfo.enable',
|
|
|
+ (newVal, oldVal) => {
|
|
|
+ nodeInfoChange(startsvg, 3)
|
|
|
+ }
|
|
|
+)
|
|
|
+watch(
|
|
|
+ () => 'nodeInfo.skipWhenFailed',
|
|
|
+ (newVal, oldVal) => {
|
|
|
+ nodeInfoChange(skipsvg, 4)
|
|
|
+ }
|
|
|
+)
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.el-input {
|
|
|
+ width: 80%;
|
|
|
+}
|
|
|
+.job-panl-icon {
|
|
|
+ vertical-align: middle;
|
|
|
+}
|
|
|
+.title {
|
|
|
+ display: inline-block;
|
|
|
+ margin: 5px 0;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+.power-dag {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+.job-panl {
|
|
|
+ /* border: 1px solid red; */
|
|
|
+ /* flex: 1; */
|
|
|
+ position: relative;
|
|
|
+ border-radius: 10px;
|
|
|
+ box-shadow: 0 10px 10px 1px #c0c0c0;
|
|
|
+ border-top-right-radius: 0px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ margin: 0 10px;
|
|
|
+ padding: 10px;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+.job-panl-btn {
|
|
|
+ /* position: absolute;
|
|
|
+ bottom: 0; */
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ width: 100%;
|
|
|
+ /* margin-right: 12px; */
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 12px;
|
|
|
+ border-top: 1px solid #f0f0f0;
|
|
|
+}
|
|
|
+.job-tools {
|
|
|
+ width: calc(100% - 10px);
|
|
|
+ box-sizing: border-box;
|
|
|
+ border: 1px solid #d0d0d0;
|
|
|
+ /* box-shadow: 0 0 10px 1px #c0c0c0; */
|
|
|
+ border-radius: 10px;
|
|
|
+ margin-right: 10px;
|
|
|
+ border-bottom-left-radius: 0px;
|
|
|
+ border-bottom-right-radius: 0px;
|
|
|
+ display: flex;
|
|
|
+ padding: 8px 10px;
|
|
|
+}
|
|
|
+.job-tools > div {
|
|
|
+ box-sizing: border-box;
|
|
|
+ height: 30px;
|
|
|
+ width: 30px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+.job-tools div:hover {
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
+}
|
|
|
+.job-tools > div + div {
|
|
|
+ margin-left: 24px;
|
|
|
+}
|
|
|
+.job-tools i {
|
|
|
+ font-size: 20px;
|
|
|
+ /* color: #aaaaaa; */
|
|
|
+}
|
|
|
+.power-import-body {
|
|
|
+ padding: 0px 20px;
|
|
|
+}
|
|
|
+/* .el-drawer__body {
|
|
|
+ padding: 0 20px;
|
|
|
+} */
|
|
|
+</style>
|
|
|
+<!-- can't use scope, or dag will be the black block, maybe this is the bug of d3.js -->
|
|
|
+<style>
|
|
|
+.power-flow {
|
|
|
+ background: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.node rect {
|
|
|
+ stroke: #999;
|
|
|
+ fill: #fff;
|
|
|
+ stroke-width: 1.5px;
|
|
|
+}
|
|
|
+
|
|
|
+.edgePath path {
|
|
|
+ stroke: #333;
|
|
|
+ stroke-width: 1px;
|
|
|
+}
|
|
|
+.power-import-table .el-table-column--selection > .cell {
|
|
|
+ padding-left: 15px;
|
|
|
+}
|
|
|
+.judge-message-params {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+.judge-message-params p {
|
|
|
+ margin-bottom: 4px;
|
|
|
+}
|
|
|
+</style>
|