Files
SPDM/src/views/task/execution/components/runDetailPage/index.vue
2026-03-09 14:34:35 +08:00

1624 lines
45 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="run-detail-page">
<div class="run-title-box" v-show="!leftFullScreen && !rightFullScreen">
<div class="task-info">
<div class="task-name">{{ currentRunNodeInfo?.runName }}</div>
<div class="task-status">
任务状态
<div class="status">
<el-icon class="upload" title="进行中" v-if="runFlowPocesInfo?.status === 'running'">
<HelpFilled />
</el-icon>
<el-icon
class="success"
title="已完成"
v-else-if="runFlowPocesInfo?.status === 'completed'"
>
<SuccessFilled />
</el-icon>
<el-icon
class="upload"
title="挂起中"
v-else-if="runFlowPocesInfo?.status === 'suspended'"
>
<UploadFilled />
</el-icon>
<el-icon
class="upload"
title="等待操作"
v-else-if="runFlowPocesInfo?.status === 'waiting_for_user'"
>
<UploadFilled />
</el-icon>
<el-icon class="warn" title="异常" v-else-if="runFlowPocesInfo?.status === 'error'">
<WarningFilled />
</el-icon>
<el-icon class="info" title="未开始" v-else>
<InfoFilled />
</el-icon>
<span class="status-title">{{
runFlowPocesInfo?.status ? statusList[runFlowPocesInfo?.status] : '未开始'
}}</span>
</div>
</div>
<div class="task-time">
作业流执行时间
<el-icon class="blue">
<Odometer />
</el-icon>
<span class="name"> {{ runFlowPocesInfo?.durationFormatted || '--' }}</span>
</div>
</div>
<div class="task-operate">
<!-- runInfo.status === RUN_STATUS.UNSTART -->
<el-button
type="primary"
v-if="!runInfo.status"
:disabled="uploadFileFlag"
@click="startTaskRunJobFun"
>开始</el-button
>
<el-button type="primary" @click="visible = true">HPC列表</el-button>
<el-button @click="refreshFun">刷新</el-button>
</div>
</div>
<div class="run-flow-box" v-show="!leftFullScreen && !rightFullScreen">
<div class="flow-box-inner" v-if="runInfo.flowTemplate && showPage">
<runFlowPage
:run-info="runInfo"
@change="changeCurrentFlowNodeFun"
@update="updateFlowPageParamListFun"
@execute="executeFun"
>
</runFlowPage>
</div>
</div>
<div :class="leftFullScreen || rightFullScreen ? 'run-info-box is-all-page' : 'run-info-box'">
<div
v-if="!rightFullScreen"
:class="leftFullScreen ? 'info-box-left allpage ' : 'info-box-left'"
>
<div class="bottom-title">
<div class="title-name">节点详情</div>
<div class="title-operate">
<el-icon
v-if="!leftFullScreen"
@click="leftFullScreen = !leftFullScreen"
class="icon-style"
><FullScreen
/></el-icon>
<el-icon v-else @click="leftFullScreen = !leftFullScreen" class="icon-style"
><Close
/></el-icon>
</div>
</div>
<div class="bottom-info-content">
<div class="tabs-box left-tab">
<el-tabs v-model="nodeActiveName" class="demo-tabs" @tab-change="handleLeftClickFun">
<el-tab-pane label="节点信息" name="info"></el-tab-pane>
<el-tab-pane label="输入数据" name="input"></el-tab-pane>
<el-tab-pane label="仿真参数" name="param"></el-tab-pane>
<el-tab-pane label="输出数据" name="output"></el-tab-pane>
</el-tabs>
</div>
<div class="operate-box">
<!-- <el-switch
v-model="executeMode"
class="mr10"
active-value="AUTO"
inactive-value="MANUAL"
active-text="自动执行"
inactive-text="手动执行"
@change="FlowNodeExecuteModeChangeFun"
/> -->
<!-- <el-button
type=""
v-if="flowNodeParamData?.nodeTypeValue === '1'"
@click="startLocalAppFun"
>启动</el-button
> -->
<!-- <el-button type="primary" @click="startLocalAppFun">执行</el-button> -->
<!-- <el-button
type="primary"
v-if="flowNodeData.nodeStatus === RUN_NODE_STATUS.WAITING_FOR_USER"
@click="continueStartRunJobFun"
>执行手动节点</el-button
> -->
<!-- <el-button
type="primary"
@click="skipCurrentNodeFlowFun"
v-if="flowNodeParamData?.nodeTypeValue === '1'"
>跳过当前本地节点</el-button
> -->
</div>
<div class="tabs-info-content">
<!-- <paramSetting
:current-run-flow-node="flowNode"
:node-param-data="nodeParamDataList"
:node-data="flowNodeParamData"
:node-info="flowNodeData"
:run-info="runInfo"
:online-file-param="onlineFileParam"
v-if="nodeActiveName === 'param' && showPage"
@update="refreshFlowNodeParamFun"
>
</paramSetting> -->
<!-- v-if="nodeActiveName === 'param' && showPage" -->
<flowNodeParamTable
ref="flowNodeParamTableRef"
v-show="nodeActiveName === 'param' && showPage"
:node-params="nodeParamDataList"
:current-node="flowNode"
:page-info="flowNodeData"
:run-info="runInfo"
:online-file-param="onlineFileParam"
:flow-node-param-data="flowNodeParamData"
:flow-node-data="flowNodeData"
@confirm="refreshFlowNodeParamFun"
@update="updateFlowNodeParamFun"
@flowclick="flowclickFun"
></flowNodeParamTable>
<runDataPage
v-show="nodeActiveName === 'input' || nodeActiveName === 'output'"
:file-id="
nodeActiveName === 'input' ? flowNodeData?.inputDirId : flowNodeData?.outputDirId
"
:node-info="flowNodeData"
></runDataPage>
<div class="bottom-info-content-tab" v-show="nodeActiveName === 'info'">
<div class="node-img">
<img :src="flowNodeParamData.iconUrl" alt="" />
</div>
<div class="node-content">
<!-- <div class="button-box">
<el-button
type="primary"
v-if="flowNodeParamData.nodeTypeValue === '1'"
@click="justStartLocalAppFun"
>启动</el-button
>
<el-button type="primary" v-else @click="startOtherNodeFun">启动</el-button>
<el-button
type="primary"
v-if="
flowNodeData.nodeStatus === 'waiting_for_user' ||
flowNodeData.nodeStatus === 'active'
"
@click="continueStartRunJobFun"
>完成</el-button
>
</div> -->
<el-form>
<el-form-item label="节点名称">
<span>{{ flowNodeData.nodeName }}</span>
</el-form-item>
<el-form-item label="节点类型">
<el-tag v-if="flowNodeParamData.nodeTypeValue === '3'" type="primary">{{
getTypeNameFun(flowNodeParamData.nodeTypeValue)
}}</el-tag>
<el-tag v-else-if="flowNodeParamData.nodeTypeValue === '2'" type="success">{{
getTypeNameFun(flowNodeParamData.nodeTypeValue)
}}</el-tag>
<el-tag v-else-if="flowNodeParamData.nodeTypeValue === '1'" type="warning">{{
getTypeNameFun(flowNodeParamData.nodeTypeValue)
}}</el-tag>
<el-tag v-else type="primary">{{
getTypeNameFun(flowNodeParamData.nodeTypeValue)
}}</el-tag>
<!-- {{ flowNodeParamData?.nodeTypeValue }} -->
</el-form-item>
<el-form-item label="节点状态">
<div class="app-status">
<div
class="round"
:style="{
background: getStyleFun(flowNodeData?.nodeStatus).color,
}"
></div>
<div class="text">
{{ getStyleFun(flowNodeData?.nodeStatus).title }}
</div>
</div>
<!-- {{ flowNodeData?.nodeDetailInfo?.status }} -->
</el-form-item>
<el-form-item label="执行时间">
<el-icon class="icon-style mr5"><Timer /></el-icon>
<span class="gl-text-ellipsis">
{{ flowNodeData?.nodeDetailInfo?.durationFormatted || '--' }}
</span>
</el-form-item>
<el-form-item label="开始时间">
<el-icon class="icon-style mr5"><Calendar /></el-icon>
<span class="gl-text-ellipsis">
{{ flowNodeData?.nodeDetailInfo?.startTime || '--' }}
</span>
</el-form-item>
<el-form-item label="完成时间">
<el-icon class="icon-style mr5"><Calendar /></el-icon>
<span class="gl-text-ellipsis">
{{ flowNodeData?.nodeDetailInfo?.endTime || '--' }}
</span>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</div>
<!-- </template> -->
<!-- <template #right> -->
<div
v-if="!leftFullScreen"
:class="rightFullScreen ? 'info-box-right allpage ' : 'info-box-right'"
>
<div class="bottom-title">
<div class="title-name">作业相关</div>
<div class="title-operate">
<el-icon
v-if="!rightFullScreen"
class="icon-style"
@click="rightFullScreen = !rightFullScreen"
><FullScreen
/></el-icon>
<el-icon v-else class="icon-style" @click="rightFullScreen = !rightFullScreen"
><Close
/></el-icon>
</div>
</div>
<div class="bottom-info-content">
<div class="tabs-box">
<el-tabs v-model="taskActiveName" class="demo-tabs" @tab-click="handleRightClickFun">
<el-tab-pane label="作业列表" name="job-list"></el-tab-pane>
<!-- <el-tab-pane label="日志输出" name="job-log"></el-tab-pane> -->
<el-tab-pane label="关联任务" name="associated-run"></el-tab-pane>
<el-tab-pane label="仿真结果" name="result"></el-tab-pane>
<!-- <el-tab-pane label="性能指标" name="performance"></el-tab-pane> -->
<el-tab-pane label="报告结果" name="report"></el-tab-pane>
<el-tab-pane label="标准规范" name="standard"></el-tab-pane>
<!-- <el-tab-pane label="3D模型预览" name="3D-model"></el-tab-pane> -->
</el-tabs>
</div>
<div class="tabs-info-content">
<resultData v-if="taskActiveName === 'result'" :current-run-ifno="runInfo"></resultData>
<jobList v-if="taskActiveName === 'job-list'" :current-run-info="runInfo"></jobList>
<taskPerformance
v-if="taskActiveName === 'performance'"
:param-type="'run'"
:run-info="runInfo"
:show-save-button="true"
:full-height="true"
></taskPerformance>
<runLogs v-if="taskActiveName === 'job-log'"></runLogs>
<ModelReview v-if="taskActiveName === '3D-model'"></ModelReview>
<runVersionTree
v-if="taskActiveName === 'associated-run'"
:current-task-info="runInfo"
></runVersionTree>
<runStandard v-if="taskActiveName === 'standard'" :run-info="runInfo"></runStandard>
<reportResult
v-if="taskActiveName === 'report'"
:current-run-ifno="runInfo"
></reportResult>
</div>
</div>
</div>
<!-- </template>
</DragSplit> -->
</div>
<HpcList v-model="visible" />
</div>
</template>
<script setup lang="ts">
import { onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue';
import resultData from './runPagecomponent/resultData.vue';
import runDataPage from './runPagecomponent/runDataPage.vue';
import jobList from './runPagecomponent/jobList.vue';
import taskPerformance from '@/components/taskDetail/taskPerformance.vue';
import runLogs from './runPagecomponent/runLogs.vue';
import ModelReview from './runPagecomponent/3DModelReview.vue';
import runVersionTree from './runPagecomponent/runVersionTree.vue';
import runFlowPage from './runPagecomponent/flow/runFlowPage.vue';
import HpcList from '@/components/task/hpcList.vue';
import {
queryRunDirApi,
retryFailedNodeApi,
saveNodeParamsApi,
startProcessInstanceApi,
uploadRunFilesApi,
} from '@/api/project/run';
import {
checklocalresourceApi,
getdeviceuuidApi,
startupPlugin,
submittaskApi,
} from '@/api/application/application';
import { ElMessage } from 'element-plus';
import { asyncCallbackApi, continueServiceTaskApi } from '@/api/flowable/process';
import runStandard from './runPagecomponent/runStandard.vue';
import { RUN_NODE_STATUS, RUN_STATUS } from '@/utils/enum/run';
import { addApplicationCallRecordApi, queryAllApplicationApi } from '@/api/system/application';
import flowNodeParamTable from '@/components/flow/flowNodeParamTable.vue';
import { cloneDeep } from 'lodash-es';
import { WIDGET_TYPE } from '@/utils/enum/flow';
import emitter from '@/utils/eventBus';
import { getDictionaryDataApi } from '@/api/system/systemData';
import { getUserData, getUserId, getUserTenantId } from '@/utils/user';
import reportResult from './runPagecomponent/reportResult.vue';
import dayjs from 'dayjs';
import { base64ToStrFun, isJSONFun } from '@/utils/file';
const props = defineProps({
runInfo: {
type: Object,
default: () => {},
},
});
const emits = defineEmits(['update']);
const statusList = ref<any>({
running: '进行中',
completed: '已完成',
suspended: '挂起中',
error: '异常',
waiting_for_user: '待操作',
});
const flowNodeParamTableRef = ref();
const executeMode = ref('MANUAL');
const visible = ref(false);
const nodeActiveName = ref('info');
const taskActiveName = ref('job-list');
const leftFullScreen = ref(false);
const rightFullScreen = ref(false);
const showPage = ref(true);
const refreshPage = ref(true);
const handleLeftClickFun = () => {};
const handleRightClickFun = () => {};
const nodeParamDataList = ref<any>([]);
const flowNode = ref<any>({});
const flowNodeData = ref<any>({});
const flowNodeParamData = ref<any>({});
const runFlowPocesInfo = ref<any>({});
const onlineFileParam = ref<any>({});
const changeCurrentFlowNodeFun = (info: any) => {
refreshPage.value = false;
const { node, data, process }: any = info;
flowNode.value = node;
flowNodeData.value = data;
flowNodeData.value.copyNodeStatus = data?.nodeStatus;
// nodeParamDataList.value = flowNode.value?.store?.data?.data?.pageConfigList || [];
nodeParamDataList.value = cloneDeep(pageParamInfo.value[data.nodeName]) || [];
flowNodeParamData.value = flowNode.value?.store?.data?.data || {};
runFlowPocesInfo.value = process;
onlineFileParam.value = {
processDefinitionId: process.processDefinitionId,
runId: data.runId,
nodeId: data.uuid,
beforeNodeId: flowNodeParamData.value?.beforeNodeId,
regexConfig: {
masterFileRegularStr: '^aa\\.xml$',
inputFilesRegularStr: '^.+\\.json$',
},
};
const params = data.userParams;
if (params?.executeMode) {
executeMode.value = params?.executeMode;
} else {
executeMode.value = 'MANUAL';
}
refreshPage.value = true;
// taskActiveName.value = 'job-list';
// nodeActiveName.value = 'info';
};
const pageParamInfo = ref<any>({});
const pageUserParam = ref<any>({});
const pageNodeDatas = ref<any>({});
const pageFlowNodeInfos = ref<any>({});
// 获取流程的每个节点的用户配置参数
const updateFlowPageParamListFun = (data: any) => {
const { list, param, nodeDatas, flowNodeInfos }: any = data;
pageParamInfo.value = list;
pageUserParam.value = param;
pageNodeDatas.value = nodeDatas;
pageFlowNodeInfos.value = flowNodeInfos;
for (const key in pageParamInfo.value) {
resetTableDataFun(pageUserParam.value[key], pageParamInfo.value[key]);
}
};
const resetTableDataFun = (obj: any, list: any) => {
for (let i = 0; i < list.length; i++) {
if (!list[i]?.englishLabel) {
if (obj[list[i].vModel] && list[i]?.children?.length) {
resetTableDataFun(obj[list[i].vModel], list[i].children);
}
} else {
for (const key in obj) {
if (list[i]?.englishLabel === key) {
list[i].defaultValue = obj[key];
}
}
}
}
};
const currentRunNodeInfo = ref<any>({});
// 开始执行任务
const startTaskRunJobFun = async () => {
// 在开始执行任务之前,保存每个节点的参数后再调用执行任务的接口
// 检查每个节点的参数
uploadFileFlag.value = 0;
for (const key in pageParamInfo.value) {
const params = pageUserParam.value[key];
const list = pageParamInfo.value[key];
const data = pageNodeDatas.value.find((item: any) => {
return item.nodeName === key;
});
const node = pageFlowNodeInfos.value.find((item: any) => {
return item.data.label === key;
})?.data;
await saveParamsFun(params, list, data, node);
}
// if (!uploadFileFlag.value) {
const res: any = await startProcessInstanceApi({
runId: props.runInfo.uuid,
});
if (res && res.code === 200) {
ElMessage.success(res.message);
showPage.value = false;
refreshRunFlowInfo();
emits('update', { status: RUN_STATUS.RUNNING });
// }
} else {
// ElMessage.success('文件正在上传中,请勿进行其他操作!');
}
};
const saveNodesParamFun = async () => {
for (const key in pageParamInfo.value) {
const params = pageUserParam.value[key];
const list = pageParamInfo.value[key];
const data = pageNodeDatas.value.find((item: any) => {
return item.nodeName === key;
});
const node = pageFlowNodeInfos.value.find((item: any) => {
return item.data.label === key;
})?.data;
await saveParamsFun(params, list, data, node);
}
};
const saveParamsFun = async (params: any, list: any, data: any, node: any) => {
const newParams: any = list;
const inputParams: any = { ...params };
const runNodeData = node;
let result: any = false;
result = checkParamRequiredFun(newParams);
if (result) {
inputParams.executeMode = 'MANUAL';
} else {
inputParams.executeMode = 'AUTO';
}
for (const key in node) {
if (['inputFormat', 'outputFormat', 'slaveFormat'].includes(key)) {
if (node[key]?.length) {
inputParams[key] = base64ToStrFun(node[key]);
} else {
inputParams[key] = '';
}
}
}
await setDataToParam(newParams, inputParams, true, data.inputDirId);
if (node.nodeType === 'HPC') {
setSpecialParamData(inputParams, runNodeData);
}
if (runNodeData.nodeType === '脚本节点' && runNodeData.label.includes('报告')) {
setReportSpecialParamDataFun(inputParams);
}
// 本地应用全部为手动节点
if (node.nodeType === '本地应用') {
inputParams.executeMode = 'MANUAL';
}
const param: any = {
nodeUuid: data.uuid,
runId: data.runId,
inputParams,
};
const res: any = await saveNodeParamsApi(param);
if (res && res.code === 200) {
}
};
const uploadFileFlag = ref(0);
const setDataToParam = async (list: any, obj: any, isUpload?: any, inputDirId?: any) => {
for (let i = 0; i < list.length; i++) {
if (list[i]?.englishLabel) {
if (list[i].tagType === WIDGET_TYPE.FILE && list[i]?.fileList?.length) {
const fileList = list[i]?.fileList || [];
let arrs: any = [];
if (fileList.length) {
uploadFileFlag.value++;
const fileData = await runUploadRunFilesFun(fileList, inputDirId);
arrs = fileData;
}
list[i].defaultValue = arrs;
}
obj[list[i].englishLabel] = list[i].defaultValue || '';
} else {
if (list[i].tagType === WIDGET_TYPE.VIEW && list[i].tagIcon === 'row') {
if (list[i]?.children?.length) {
obj[list[i].vModel] = {};
setDataToParam(list[i]?.children, obj[list[i].vModel], isUpload);
}
}
}
}
};
const runUploadRunFilesFun = async (list: any, inputDirId: any) => {
const sourceFiles = list.map((item: any) => {
return {
fileName: item.name,
size: item.size,
fileType: '',
};
});
const param = {
sourceFiles,
uploadTaskId: new Date().getTime(),
dirId: inputDirId,
};
const res: any = await uploadRunFilesApi(param);
if (res && res.code === 200) {
res.data.forEach((item: any, index: any) => {
item.isSaveLocal = 'Y';
emitter.emit('ADD_UPLOAD_FILE', {
file: list[index].raw,
data: {
// 接口返回的文件目录信息,包括配置信息
...item,
isApprove: 0, // 0否 1是
taskType: 4, // 4交付物
callbackFlag: 'START_FLOW',
},
});
});
}
if (res.code === 200) {
const result = res.data.map((item: any) => {
// return item.objectKey;
const path = item.objectKey.split('/').slice(0, -1).concat(item.sourceFileName).join('/');
return path;
});
return result;
}
};
// 检查是否必填参数都填写
const checkParamRequiredFun = (list: any) => {
for (let i = 0; i < list.length; i++) {
if (list[i].required) {
if (list[i].tagType === WIDGET_TYPE.FILE) {
if (!list[i]?.fileList?.length && !list[i].defaultValue?.length) {
return true;
}
} else {
if (!list[i].defaultValue) {
return true;
}
}
} else {
if (list[i]?.children?.length) {
checkParamRequiredFun(list[i]?.children);
}
}
}
};
// 设置特殊的参数信息
const setSpecialParamData = (param: any, data: any) => {
param.taskId = props.runInfo.taskId;
param.taskName = props.runInfo.parentName;
param.runId = props.runInfo.uuid;
param.runName = props.runInfo.runName;
param.projectname = props.runInfo.projectName;
// param.software = '电池仿真';
// param.jobName = '测试任务' + new Date().getTime();
if (!param.jobName) {
param.jobName = '测试任务' + new Date().getTime();
}
param.software = data.label;
const keys = Object.keys(param);
if (keys.includes('masterFileRegularStr') && keys.includes('inputFilesRegularStr')) {
param.explicitInputFiles = {
masterFileRegularStr: param.masterFileRegularStr.length ? param.masterFileRegularStr : [],
inputFilesRegularStr: param.inputFilesRegularStr.length ? param.inputFilesRegularStr : [],
};
}
};
// 报告脚本生成参数填写
const setReportSpecialParamDataFun = (param: any) => {
const projectInfo = JSON.parse(
localStorage.getItem('CURRENT_FILTER_RUN_TASK_TREE_PARAM') as string
);
param.reportName = '算例报告_' + dayjs().format('YYYY_MM_DD_HH_mm');
param.applicants = getUserData().nickname;
param.date = dayjs().format('YYYY-MM-DD HH:mm:ss');
param.loadcaseName = props.runInfo.parentName;
param.projectNum = projectInfo?.projectCode;
param.formulateTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
param.checkTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
param.approveTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
param.workspace = '';
param.workspaceNum = '';
};
const continueStartRunJobFun = async () => {
if (uploadFileFlag.value) {
ElMessage.warning('等待文件上传完毕后再执行流程!');
return;
}
if (flowNodeData.value.nodeStatus === 'active') {
await skipCurrentNodeFlowFun();
}
const res: any = await continueServiceTaskApi({
processInstanceId: flowNodeData.value.processInstanceId,
taskDefinitionKey: flowNodeData.value.nodeId,
flowelementType: flowNodeData.value.nodeDetailInfo.type,
});
if (res && res.code === 200) {
showPage.value = false;
refreshRunFlowInfo();
}
};
// 启动应用
const startLocalAppFun = async () => {
if (flowNodeData.value?.nodeStatus === RUN_NODE_STATUS.WAITING_FOR_USER) {
continueStartRunJobFun();
} else if (flowNodeData.value?.nodeStatus === RUN_NODE_STATUS.ERROR) {
// 重新执行失败的流程节点
// 重新保存该流程节点的参数
await flowNodeParamTableRef.value.saveNodeParamFun('RESTART_FLOW_NODE');
// 调用重新执行的接口
} else {
// 本地节点拉起应用
if (flowNodeParamData.value?.nodeTypeValue === '1') {
await justStartLocalAppFun();
}
}
};
const getAppInfo = async (uuid: any) => {
const param = {
appName: '',
current: 1,
size: 999,
};
try {
const res: any = await queryAllApplicationApi(param);
if (res && res.code === 200) {
for (let i = 0; i < res.data.data.length; i++) {
const paths = isJSONFun(res.data.data[i].appPath)
? JSON.parse(res.data.data[i].appPath as string)
: res.data.data[i].appPath;
if (paths && paths instanceof Object) {
const keys = Object.keys(paths);
if (keys.includes(getUserId())) {
res.data.data[i].appPath = paths[getUserId()];
} else {
res.data.data[i].appPath = '';
}
}
}
const appPath = res.data.data.find((item: any) => {
return item.uuid === uuid;
})?.appPath;
return appPath;
} else {
return '';
}
} catch {
return '';
}
};
const justStartLocalAppFun = async () => {
if (uploadFileFlag.value) {
ElMessage.success('等待文件传输完成后再进行操作');
return;
}
// if (flowNodeParamData.value?.nodeTypeValue === '1') {
// const appPath = flowNodeParamData.value.appPath || '';
const appUuid = flowNodeParamData.value.uuid;
const appPath = (await getAppInfo(appUuid)) || '';
await getdeviceuuidFun();
const uuid = localStorage.getItem('USER_UUID');
if (uuid) {
const result: any = await checklocalresourceFun(appPath);
if (result) {
const params = await getSubmitParamFun(appPath);
const res: any = await submittaskApi(params);
if (res && res.code === 200) {
const res2: any = await addApplicationCallRecordApi({
appName: flowNodeParamData.value.label,
appType: flowNodeParamData.value.nodeTypeValue,
creator: flowNodeParamData.value.creator,
});
if (res2 && res2.code === 200) {
}
ElMessage.success('应用正在启动中,请等待');
refreshFun();
} else {
}
}
} else {
return;
}
// }
};
// 启动其他应用
const startOtherNodeFun = async () => {
if (uploadFileFlag.value) {
ElMessage.success('等待文件传输完成后再进行操作');
return;
}
if (flowNodeData.value?.nodeStatus === RUN_NODE_STATUS.WAITING_FOR_USER) {
continueStartRunJobFun();
} else if (flowNodeData.value?.nodeStatus === RUN_NODE_STATUS.ERROR) {
await flowNodeParamTableRef.value.saveNodeParamFun('RESTART_FLOW_NODE');
}
};
// 重新执行节点按钮
const retryFailedNodeFun = async () => {
const param = {
processInstanceId: flowNodeData.value.processInstanceId,
failNodeId: flowNodeData.value.nodeId,
};
try {
const res: any = await retryFailedNodeApi(param);
if (res && res.code === 200) {
ElMessage.success('节点已重新执行!');
showPage.value = false;
refreshRunFlowInfo();
}
} catch (error) {
console.error(error);
}
};
// 获取插件提交计算的参数信息
const getSubmitParamFun = async (appPath: any) => {
const taskName = props.runInfo?.parentName;
const taskId = props.runInfo?.taskId;
const taskCmd = appPath;
const taskPreCmd = flowNodeParamData.value.preExeCommand;
const taskPostCmd = flowNodeParamData.value.postExeCommand;
const runId = props.runInfo?.uuid;
const runName = props.runInfo?.runName;
const taskPreCmdParam = {
'load.py': '',
};
const taskPostCmdParam = {
'pptx.py': '',
};
const { fileIds, fileNames, taskCmdParam }: any = await getNodeFileIdAndNames();
const fileType = await getfileTypeList();
const uploadId = flowNodeData.value.userParams.outputDirId.toString();
const owner = getUserData()?.nickname;
const tenantId = getUserTenantId().toString();
const userId = getUserId().toString();
const localAppName = flowNodeData.value.nodeName;
const localAppVersion = '';
const creatorId = getUserId().toString();
const subRunId = flowNodeData.value.nodeId;
const asyncTaskId = flowNodeData.value.nodeDetailInfo?.asyncTaskId;
const outputFormat = flowNodeParamData.value.outputFormat;
return {
runId,
runName,
taskName,
taskId,
taskCmd,
taskPreCmd,
taskPostCmd,
taskCmdParam,
taskPostCmdParam,
taskPreCmdParam,
fileIds,
fileNames,
fileType,
uploadId,
owner,
tenantId,
userId,
localAppName,
localAppVersion,
creatorId,
subRunId,
asyncTaskId,
outputFormat,
};
};
const getNodeFileIdAndNames = async () => {
const dirId = flowNodeData.value.userParams.inputDirId;
const fileIds: any = [];
const fileNames: any = [];
const taskCmdParam: any = {};
// 本地应用执行的脚本也需要下载下来
if (flowNodeParamData.value?.postScripts?.length) {
for (let i = 0; i < flowNodeParamData.value?.postScripts?.length; i++) {
fileIds.push(flowNodeParamData.value?.postScripts[i].fileId);
fileNames.push(flowNodeParamData.value?.postScripts[i].name);
}
}
if (flowNodeParamData.value?.preScripts?.length) {
for (let i = 0; i < flowNodeParamData.value?.preScripts?.length; i++) {
fileIds.push(flowNodeParamData.value?.preScripts[i].fileId);
fileNames.push(flowNodeParamData.value?.preScripts[i].name);
}
}
try {
const res: any = await queryRunDirApi({
current: 1,
fileId: dirId,
size: 999,
});
if (res && res.code === 200) {
const list = res.data.data;
for (let i = 0; i < list.length; i++) {
fileIds.push(list[i].id.toString());
fileNames.push(list[i].originalName);
}
// if (flowNodeParamData.value.nodeExeCommand) {
// taskCmdParam[flowNodeParamData.value.nodeExeCommand] = './' + fileNames.join(',');
// // = fileNames.map((item: any) => {
// // return { [flowNodeParamData.value.nodeExeCommand]: item };
// // });
// } else {
// taskCmdParam = {};
// }
if (Object.keys(flowNodeData.value.userParams).includes('--export')) {
taskCmdParam['--export'] = flowNodeData.value.userParams['--export'];
}
if (Object.keys(flowNodeData.value.userParams).includes('--import')) {
taskCmdParam['--import'] = './' + fileNames.join(',');
}
return {
fileIds,
fileNames,
taskCmdParam,
};
} else {
return {
fileIds,
fileNames,
taskCmdParam,
};
}
} catch {
return {
fileIds,
fileNames,
taskCmdParam,
};
}
};
const getfileTypeList = async () => {
try {
const res: any = await getDictionaryDataApi({ dictClass: 'FILE_UPLOAD_ALLOCATION_TYPE' });
if (res && res.code === 200) {
// const types: any = res.data.map((item: any) => {
// const obj: any = {};
// obj[item.dictName] = item.dictValue.split(',');
// return obj;
// });
const obj: any = {};
for (let i = 0; i < res.data.length; i++) {
obj[res.data[i].dictName] = res.data[i].dictValue.split(',');
}
const outPutFile = flowNodeData.value?.userParams?.outputFileType?.split(',') || [];
if (outPutFile?.length) {
const list: any = [];
for (let i = 0; i < outPutFile.length; i++) {
let isReturnFile = true;
for (const key in obj) {
if (obj[key].includes(outPutFile[i])) {
isReturnFile = false;
}
}
if (isReturnFile) {
list.push(outPutFile[i]);
}
}
obj['1'] = obj['1'].concat(list);
}
return obj;
} else {
return {};
}
} catch {
return {};
}
};
// 检查资源是否合法
const checklocalresourceFun = async (path: any) => {
try {
const res: any = await checklocalresourceApi({ resource: [path] });
if (res && res.code === 200) {
if (res.data.returnCode.includes('Operate Success')) {
return true;
} else {
return false;
}
}
} catch {
return false;
}
};
const getdeviceuuidFun = async () => {
try {
const res: any = await getdeviceuuidApi();
if (res && res.code === 200) {
const uuid = res.data.returnCode
.replace('UUID', '')
.replace(/\ +/g, '')
.replace(/[\r\n]/g, '');
localStorage.setItem('USER_UUID', uuid);
}
} catch {
startupPlugin();
}
};
// 刷新节点页面信息
const refreshRunFlowInfo = () => {
setTimeout(() => {
showPage.value = true;
});
};
const refreshFun = () => {
showPage.value = false;
refreshRunFlowInfo();
};
// const FlowNodeExecuteModeChangeFun = async () => {
// const inputParams = {
// ...flowNodeData.value.userParams,
// executeMode: executeMode.value,
// };
// const params = {
// nodeUuid: flowNodeParamData.value?.flowNodeInfo?.uuid,
// runId: props.runInfo.uuid,
// inputParams,
// };
// try {
// const res: any = await saveNodeParamsApi(params);
// if (res && res.code === 200) {
// ElMessage.success('操作成功');
// refreshFun();
// } else {
// }
// } catch {}
// };
const refreshFlowNodeParamFun = async (data: any) => {
const { flag }: any = data;
if (flag === false) {
await retryFailedNodeFun();
showPage.value = false;
refreshRunFlowInfo();
}
if (flag === true) {
ElMessage.success('文件正在上传中,请等待文件上传完成后再操作');
}
};
const newNodeData = ref<any>({});
const updateFlowNodeParamFun = (data: any) => {
for (let i = 0; i < pageParamInfo.value[flowNodeData.value.nodeName].length; i++) {
for (const key in data[i]) {
pageParamInfo.value[flowNodeData.value.nodeName][i][key] = data[i][key];
}
}
newNodeData.value[flowNodeData.value.nodeName] = data;
// nodeParamDataList.value = data;
};
const getStyleFun = (flag: any) => {
const styles: any = {
active: {
color: '#409eff',
title: '进行中',
},
waiting_for_user: {
color: '#409eff',
title: '进行中',
},
finished: {
color: '#67c23a',
title: '已完成',
},
pending: {
color: '#909399',
title: '未开始',
},
};
if (styles[flag]) {
return styles[flag];
} else {
return {
color: '#f56c6c',
title: '异常',
};
}
};
const getTypeNameFun = (type: any) => {
const str =
appTypeList.value.find((item: any) => {
return item.dictValue === type;
})?.dictName || '';
return str;
};
const appTypeList = ref<any>([]);
const getDictionaryDataFun = async () => {
const res: any = await getDictionaryDataApi({
dictClass: 'APP_TYPE',
});
if (res && res.code === 200) {
appTypeList.value = res.data;
}
};
const skipCurrentNodeFlowFun = async () => {
const params = {
asyncTaskId: flowNodeData.value?.nodeDetailInfo.asyncTaskId,
resultJson: 'success',
status: 'SUCCESS',
};
try {
const res: any = await asyncCallbackApi(params);
if (res && res.code === 200) {
// refreshFun();
}
} catch (error) {
console.error(error);
}
};
// 双击两次执行当前应用节点
const executeFun = () => {
if (uploadFileFlag.value) {
ElMessage.success('文件未上传完整,请等待稍后再进行操作');
return;
}
startLocalAppFun();
};
// 文件上传完成后的回调事件
const uploadFinishedFun = async (data: any) => {
// 开始任务执行
if (data.callbackFlag === 'START_FLOW') {
uploadFileFlag.value--;
// const res: any = await startProcessInstanceApi({
// runId: props.runInfo.uuid,
// });
// if (res && res.code === 200) {
// ElMessage.success(res.message);
// showPage.value = false;
// refreshRunFlowInfo();
// emits('update', { status: RUN_STATUS.RUNNING });
// }
}
// 重复执行失败节点
if (data.callbackFlag === 'RESTART_FLOW_NODE') {
await retryFailedNodeFun();
}
};
const flowclickFun = async (flag: any) => {
await saveNodesParamFun();
if (flag === 'justStartLocalAppFun') {
await justStartLocalAppFun();
}
if (flag === 'startOtherNodeFun') {
await startOtherNodeFun();
}
if (flag === 'continueStartRunJobFun') {
await continueStartRunJobFun();
}
};
watch(
() => props.runInfo,
(newVal) => {
if (newVal) {
currentRunNodeInfo.value = newVal;
}
},
{
immediate: true,
deep: true,
}
);
const timer = ref<any>(null);
onMounted(async () => {
await getDictionaryDataFun();
emitter.on('UPLOAD_FINISHED', uploadFinishedFun);
// 5分钟自动刷新一次流程信息
timer.value = setInterval(
() => {
refreshFun();
},
5 * 60 * 1000
);
});
onBeforeUnmount(() => {
emitter.off('UPLOAD_FINISHED', uploadFinishedFun);
clearInterval(timer.value);
});
onActivated(() => {
emitter.on('UPLOAD_FINISHED', uploadFinishedFun);
});
onDeactivated(() => {
emitter.off('UPLOAD_FINISHED', uploadFinishedFun);
});
</script>
<style lang="scss" scoped>
.run-detail-page {
width: 100%;
height: 100%;
position: relative;
.run-title-box {
width: 100%;
height: 50px;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
padding-right: 10px;
padding-left: 10px;
margin-bottom: 10px;
border-radius: 2px;
.task-info {
height: 100%;
display: flex;
align-items: center;
.task-name {
padding-left: 10px;
border-left: 3px solid var(--el-color-primary);
font-size: 16px;
font-weight: 600;
margin-right: 20px;
}
.task-executor {
height: 100%;
display: flex;
align-items: center;
font-size: 14px;
margin-right: 20px;
}
.task-status {
height: 100%;
display: flex;
align-items: center;
font-size: 14px;
margin-right: 20px;
.status {
height: 100%;
display: flex;
align-items: center;
}
}
.task-time {
height: 100%;
display: flex;
align-items: center;
font-size: 14px;
margin-right: 20px;
}
.name {
font-weight: 600;
}
}
}
.run-flow-box {
width: 100%;
height: 200px;
background-color: #fff;
margin-bottom: 10px;
border-radius: 2px;
overflow: hidden;
.flow-box-inner {
width: 100%;
height: 100%;
}
}
.run-info-box {
width: 100%;
height: calc(100% - 270px);
display: flex;
align-items: center;
justify-content: space-between;
.run-flow-info-box {
width: 100%;
height: 180px;
margin-bottom: 10px;
border-radius: 2px;
background-color: #fff;
.bottom-title {
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 10px;
.title-name {
padding-left: 10px;
border-left: 3px solid var(--el-color-primary);
font-size: 14px;
font-weight: 600;
}
.title-operate {
height: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 10px;
.icon-style {
font-size: 16px;
color: var(--el-color-primary);
cursor: pointer;
}
}
}
.bottom-info-content {
width: 100%;
height: calc(100% - 40px);
padding: 0 10px;
display: flex;
align-items: center;
justify-content: space-between;
.node-img {
width: 100px;
height: 100%;
display: flex;
justify-content: center;
img {
width: 100px;
height: 100px;
background-color: #f2f2f2;
border-radius: 5px;
}
}
.node-content {
width: calc(100% - 140px);
height: 100%;
// .el-form {
// width: 100%;
// height: 100%;
// flex-wrap: wrap;
// .el-form-item {
// // width: calc(100% / 4);
// }
// }
}
}
}
.info-box-left,
.info-box-right {
width: calc(50% - 5px);
// width: 100%;
// height: 500px;
height: 100%;
background-color: #fff;
border-radius: 2px;
// margin-bottom: 10px;
// overflow: hidden;
.bottom-title {
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 10px;
.title-name {
padding-left: 10px;
border-left: 3px solid var(--el-color-primary);
font-size: 14px;
font-weight: 600;
}
.title-operate {
height: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 10px;
.icon-style {
font-size: 16px;
color: var(--el-color-primary);
cursor: pointer;
}
}
}
.bottom-info-content {
width: 100%;
height: calc(100% - 40px);
padding: 0 10px;
position: relative;
.operate-box {
position: absolute;
top: 0;
right: 20px;
}
.tabs-box {
// width: calc(100% - 220px);
height: 40px;
}
// .left-tab {
// width: calc(100% - 140px);
// }
.tabs-info-content {
width: 100%;
height: calc(100% - 50px);
padding: 10px;
overflow: auto;
}
}
}
.allpage {
width: 100%;
height: 100%;
}
}
.is-all-page {
width: 100%;
height: 100%;
left: 0;
top: 0;
z-index: 20;
position: absolute;
}
.blue {
color: var(--el-color-primary);
margin-right: 5px;
}
}
.app-status {
width: 60px;
display: flex;
align-items: center;
.round {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #191919;
margin-right: 5px;
}
.text {
font-size: 12px;
}
}
.point {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-title {
padding-left: 8px;
}
.success {
color: #67c23a;
}
.error {
color: #f56c6c;
}
.mr5 {
margin-right: 5px;
}
.info {
color: grey;
}
.success {
color: #67c23a;
}
.warn {
color: #f56c6c;
}
.upload {
color: #409eff;
}
.mr10 {
margin-right: 10px;
}
.icon-style {
font-size: 16px;
color: var(--el-color-primary);
cursor: pointer;
}
.mr5 {
margin-right: 5px;
}
.bottom-info-content-tab {
width: 100%;
// height: calc(100% - 40px);
height: 100%;
padding: 0 10px;
padding-right: 0;
display: flex;
align-items: center;
justify-content: space-between;
// padding: 20px;
.node-img {
width: 100px;
height: 100%;
display: flex;
justify-content: center;
img {
width: 100px;
height: 100px;
background-color: #f2f2f2;
border-radius: 5px;
}
}
.node-content {
width: calc(100% - 140px);
height: 100%;
padding: 0 20px;
padding-right: 0;
padding-bottom: 0;
.el-form {
width: 100%;
// height: 100%;
height: calc(100% - 50px);
overflow: auto;
flex-wrap: wrap;
.el-form-item {
// width: calc(100% / 3);
}
}
.button-box {
width: 100%;
height: 50px;
display: flex;
align-items: center;
justify-content: flex-end;
}
}
}
</style>