Files
SPDM/src/components/flow/flowNodeParamTable.vue

745 lines
21 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="flow-node-param-page">
<div class="table-box">
<!-- :data="tableData" -->
<BaseTable
ref="baseTableRef"
hide-pagination
tableName="FLOW_NODE_PARAM"
:show-overflow="false"
:show-node-name="'label'"
:tree-config="{
expandAll: true,
rowField: 'vModel',
childrenField: 'children',
}"
:full-height="true"
:show-setting="false"
>
<template #leftOptions v-if="!hideButtons">
<el-button
type="primary"
v-if="flowNodeParamData.nodeTypeValue === '1'"
@click="flowClickFun('justStartLocalAppFun')"
>启动</el-button
>
<el-button
type="primary"
v-else-if="flowNodeParamData.nodeTypeValue === '3'"
@click="flowClickFun('startOtherNodeFun')"
>计算</el-button
>
<el-button
type="primary"
v-else-if="flowNodeParamData.nodeTypeValue === '5'"
@click="flowClickFun('startOtherNodeFun')"
>保存</el-button
>
<el-button type="primary" v-else @click="flowClickFun('startOtherNodeFun')"
>启动</el-button
>
<el-button
type="primary"
v-if="
flowNodeData.nodeStatus === 'waiting_for_user' || flowNodeData.nodeStatus === 'active'
"
@click="flowClickFun('continueStartRunJobFun')"
>完成</el-button
>
<el-button v-if="flowNodeParamData.nodeTypeValue === '1'" @click="showLogFun"
>日志</el-button
>
<el-dropdown>
<el-button type="primary" class="ml10">
参数操作<el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>参数引用</el-dropdown-item>
<el-dropdown-item>参数入库</el-dropdown-item>
<el-dropdown-item>参数导入</el-dropdown-item>
<el-dropdown-item>参数导出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- <el-button type="primary" @click="referenceParamFun">引用参数</el-button>
<el-button type="primary" @click="paramToLibFun">参数入库</el-button> -->
<!-- <el-button type="primary" @click="saveNodeParamFun">保存参数</el-button> -->
</template>
<template #value="{ row }">
<div class="pr10 tableCellContent">
<!-- 输入框 -->
<div
class="numInput flex-div"
v-if="
(row.tagType === WIDGET_TYPE.INPUTS || row.tagType === WIDGET_TYPE.INPUT) &&
row.englishLabel
"
>
<div
class="input-grid"
:style="{
display: row.total === row.column ? 'flex' : 'grid',
gridTemplateColumns: 'repeat(' + row.column + ',1fr)',
}"
>
<div class="flex-align-center mr12">
<el-input
class="span-min80"
v-model="row.defaultValue"
:disabled="readonly"
@change="updateRowInfo"
></el-input>
</div>
</div>
</div>
<!-- 2下拉框-->
<el-select
v-if="row.tagType === WIDGET_TYPE.SELECT"
:multiple="row.isMultiple === '1'"
:title="row.defaultValue"
v-model="row.defaultValue"
class="w70p"
collapse-tags
:allow-create="row.importability === '1'"
filterable
default-first-option
:disabled="readonly"
>
<el-option
v-for="item in row?.options || []"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<!-- 4单选框 -->
<el-checkbox
v-if="row.tagType === WIDGET_TYPE.CHECKBOX"
v-model="row.defaultValue"
true-value="1"
false-value="0"
:disabled="readonly"
/>
<!-- 多选框 -->
<div v-if="row.tagType === WIDGET_TYPE.CHECKBOXS">
<el-checkbox-group v-model="row.defaultValue" :disabled="readonly">
<el-checkbox
v-for="item in row.options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-checkbox-group>
</div>
<!-- 计数器 -->
<div
class="numInput"
v-if="
row.tagType === WIDGET_TYPE.NUMBER_INPUTS ||
row.tagType === WIDGET_TYPE.NUMBER_INPUT
"
>
<div class="input-grid">
<el-input-number
class="mr10"
controls-position="right"
v-model="row.defaultValue"
:disabled="readonly"
></el-input-number>
<span class="over-ellipsis span-fommat" :title="row?.units">{{ row?.unit }}</span>
</div>
</div>
<!-- 6单纯显示内容无法编辑 -->
<div v-if="row.tagType === WIDGET_TYPE.VIEW && row.tagIcon === 'input'">
{{ row.defaultValue }}
</div>
<!-- 滑块 -->
<div v-if="row.tagType === WIDGET_TYPE.SLIDER" class="slider">
<span class="value">{{ row.defaultValue }}</span>
<el-slider
v-model="row.defaultValue"
:min="row.min"
:max="row.max"
size="small"
:disabled="readonly"
/>
</div>
<!-- 9 模型文件选择 local + server-->
<div
class="localServer"
v-if="
row.tagType === WIDGET_TYPE.FILE ||
row.tagType === NODE_TYPE.SCRIPT ||
row.tagType === NODE_TYPE.PPT
"
>
<flowFileUpload
ref="flowFileUploadRef"
:row="row"
:dir-id="pageInfo?.inputDirId"
:online-file-param="onlineFileParam"
:readonly="readonly"
@update="updateRowInfo"
:setect-type="2"
:select-file-list="selectFileList"
></flowFileUpload>
<!-- <span>{{ getFileNameList(row.defaultValue) }}</span> -->
</div>
<!-- 行容器 -->
<!-- <div v-if="row.tagType === WIDGET_TYPE.VIEW && row.tagIcon === 'row'">
{{ '' }}
</div> -->
</div>
</template>
<template #label="{ row }">
<span v-if="row.required" class="required-icon">*</span>
<span v-else class="required-icon">&nbsp;</span>
<span v-if="row.tagType === WIDGET_TYPE.VIEW && row.tagIcon === 'row'">
{{ row?.children[0]?.defaultValue || '配置' }}
</span>
<span v-else>{{ row.label }}</span>
</template>
</BaseTable>
</div>
<!-- <div class="operate-box">
<el-button type="primary" @click="referenceParamFun">引用参数</el-button>
<el-button type="primary" @click="paramToLibFun">参数入库</el-button>
</div> -->
<parameterArchive
v-if="parameterArchiveVisible"
:param-info="paramData"
@close="parameterArchiveVisible = false"
>
</parameterArchive>
<parameterReference
v-if="parameterReferenceVisible"
@close="parameterReferenceVisible = false"
@update="updateParamInfoFun"
>
</parameterReference>
<Dialog
v-model="showRunLog"
diaTitle="日志详情"
:width="'60%'"
:height="'70%'"
@close="handleCloseFun"
>
<runLogs v-if="showRunLog" :flow-task-id="flowNodeData.nodeId"></runLogs>
</Dialog>
</div>
</template>
<script setup lang="ts">
import { ref, watch, nextTick } from 'vue';
import BaseTable from '@/components/common/table/baseTable.vue';
import { NODE_TYPE, WIDGET_TYPE } from '@/utils/enum/flow';
import { queryRunDirApi, saveNodeParamsApi, uploadRunFilesApi } from '@/api/project/run';
import { cloneDeep } from 'lodash-es';
import flowFileUpload from './flowFileUpload.vue';
import parameterArchive from '@/views/task/execution/components/runDetailPage/runPagecomponent/parameterArchive.vue';
import parameterReference from '@/views/task/execution/components/runDetailPage/runPagecomponent/parameterReference.vue';
import { ElMessage } from 'element-plus';
import emitter from '@/utils/eventBus';
import { base64ToStrFun, fileUploadAllocationTypeFun } from '@/utils/file';
import { getUserData } from '@/utils/user';
import dayjs from 'dayjs';
import Dialog from '@/components/common/dialog/index.vue';
import runLogs from '@/views/task/execution/components/runDetailPage/runPagecomponent/runLogs.vue';
const props = defineProps({
nodeParams: {
type: Array,
default: () => [],
},
pageInfo: {
type: Object,
},
currentNode: {
type: Object,
},
runInfo: {
type: Object,
default: () => {},
},
hideButtons: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: false,
},
onlineFileParam: {
type: Object,
default: () => {},
},
flowNodeData: {
type: Object,
default: () => {},
},
flowNodeParamData: {
type: Object,
default: () => {},
},
});
const emits = defineEmits(['update', 'confirm', 'flowclick']);
const flowFileUploadRef = ref();
const baseTableRef = ref();
const tableData = ref<any>([]);
const parameterArchiveVisible = ref(false);
const parameterReferenceVisible = ref(false);
// 格式化输入的数据信息
const formatDataFun = (list: any, parentId: any) => {
for (let i = 0; i < list.length; i++) {
list[i].parentId = parentId;
// 多选框组的默认值是勾选的组件的selectList参数
if (list[i].tagType === WIDGET_TYPE.CHECKBOXS) {
list[i].defaultValue = list[i]?.selectList;
}
if (list[i]?.children?.length) {
formatDataFun(list[i]?.children, list[i].vModel);
}
}
};
const uploadFileFlag = ref(false);
const checkParamRequired = ref(false);
// 保存参数
const saveNodeParamFun = async (flag?: any) => {
uploadFileFlag.value = false;
const runNodeInfo = props.currentNode?.store?.data?.data?.flowNodeInfo;
const runNodeData = props.currentNode?.store?.data?.data;
const newParams: any = cloneDeep(tableData.value);
const userParams = props.pageInfo?.userParams || {};
const inputParams: any = { ...userParams };
checkParamRequiredFun(newParams);
if (checkParamRequired.value) {
// ElMessage.warning('存在必填项没有填写!');
checkParamRequired.value = false;
// return;
inputParams.executeMode = 'MANUAL';
} else {
inputParams.executeMode = 'AUTO';
}
for (const key in runNodeData) {
if (['inputFormat', 'outputFormat', 'slaveFormat'].includes(key)) {
if (runNodeData[key]?.length) {
inputParams[key] = base64ToStrFun(runNodeData[key]);
} else {
inputParams[key] = '';
}
}
}
await setDataToParam(newParams, inputParams, true, flag);
if (runNodeData.nodeType === 'HPC') {
setSpecialParamData(inputParams, runNodeData);
}
if (runNodeData.nodeType === '脚本节点' && runNodeData.label.includes('报告')) {
setReportSpecialParamDataFun(inputParams);
}
const params: any = {
nodeUuid: runNodeInfo.uuid,
runId: runNodeInfo.runId,
inputParams,
};
const res: any = await saveNodeParamsApi(params);
if (res && res.code === 200) {
ElMessage.success('保存成功');
emits('confirm', { flag: uploadFileFlag.value });
}
};
// 检查是否必填参数都填写
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) {
checkParamRequired.value = true;
}
} else {
if (!list[i].defaultValue) {
checkParamRequired.value = true;
}
}
} else {
if (list[i]?.children?.length) {
checkParamRequiredFun(list[i]?.children);
}
}
}
};
const setDataToParam = async (list: any, obj: any, isUpload?: any, flag?: 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) {
const fileData = await runUploadRunFilesFun(fileList, flag);
arrs = fileData;
uploadFileFlag.value = true;
// for (let j = 0; j < fileList.length; j++) {
// if (isUpload) {
// await uploadRunFilesFun(fileList[j].raw);
// }
// arrs.push({ fileName: fileList[j].name });
// }
}
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, flag);
}
}
}
}
};
const runUploadRunFilesFun = async (list: any, flag: any) => {
const sourceFiles = [];
for (let i = 0; i < list.length; i++) {
sourceFiles.push({
fileName: list[i].name,
size: list[i].size,
fileType: fileUploadAllocationTypeFun(list[i].name),
});
}
// = list.map(async (item: any) => {
// return {
// fileName: item.name,
// size: item.size,
// fileType: await fileUploadAllocationTypeFun(item.name),
// };
// });
const param = {
sourceFiles,
uploadTaskId: new Date().getTime(),
dirId: props.pageInfo?.inputDirId,
isConverSameNameFile: true,
};
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: flag,
},
});
});
}
if (res.code === 200) {
const result = res.data.map((item: any) => {
const path = item.objectKey.split('/').slice(0, -1).concat(item.sourceFileName).join('/');
return path;
// return item.objectKey;
});
return result;
}
};
// const getFileNameList = (list: any) => {
// let names: any = '';
// if (list?.length) {
// names = list
// .map((item: any) => {
// return item.split('/').at(-1);
// })
// .join(',');
// }
// return names;
// };
// 参数入库
const paramData = ref<any>([]);
const paramToLibFun = async () => {
const data: any = cloneDeep(tableData.value);
paramData.value = [];
getParamData(data);
parameterArchiveVisible.value = true;
};
const getParamData = (list: any) => {
for (let i = 0; i < list.length; i++) {
if (list[i].tagType === WIDGET_TYPE.VIEW && list[i].tagIcon === 'row') {
if (list[i]?.children?.length) {
getParamData(list[i]?.children);
}
} else {
paramData.value.push({
parameterName: list[i].englishLabel,
parameterValue: JSON.stringify(list[i].defaultValue),
unit: list[i].unit,
description: list[i]?.label,
});
}
}
};
//
const referenceParamFun = () => {
parameterReferenceVisible.value = true;
};
const updateParamInfoFun = (data: any) => {
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < tableData.value.length; j++) {
if (data[i].parameterName === tableData.value[j].englishLabel) {
tableData.value[j].defaultValue = JSON.parse(data[i].parameterValue);
}
}
}
parameterReferenceVisible.value = false;
};
// 设置特殊的参数信息
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 = 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
);
if (!param.reportName) {
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 updateRowInfo = (data: any) => {
for (let i = 0; i < tableData.value.length; i++) {
if (
tableData.value[i].englishLabel === data.englishLabel &&
tableData.value[i].label === data.label
) {
for (const key in data) {
tableData.value[i][key] = data[key];
}
}
}
emits('update', tableData.value);
};
const flowClickFun = (flag: any) => {
emits('flowclick', flag);
};
const selectFileList = ref<any>([]);
const queryFileList = async (id: any) => {
const param = {
fileId: id,
current: 1,
size: 999,
};
try {
const res: any = await queryRunDirApi(param);
if (res && res.code === 200) {
selectFileList.value = res.data.data.map((item: any) => {
const filePath = item.objectKey.split('/').slice(0, -1).concat(item.originalName).join('/');
return {
...item,
filePath,
};
});
}
} catch {}
};
const showRunLog = ref(false);
const showLogFun = () => {
showRunLog.value = true;
};
const handleCloseFun = () => {
showRunLog.value = false;
};
defineExpose({
saveNodeParamFun,
paramToLibFun,
referenceParamFun,
queryFileList,
flowFileUploadRef,
});
watch(
() => props.nodeParams,
(newVal) => {
if (newVal) {
// const paramData = nodeParamData.value?.pageConfigList || [];
tableData.value = cloneDeep(newVal);
// tableData.value = newVal;
for (let i = 0; i < tableData.value.length; i++) {
if (tableData.value[i].englishLabel === 'jobName') {
tableData.value[i].defaultValue = '测试任务' + new Date().getTime();
}
if (tableData.value[i].englishLabel === 'reportName') {
tableData.value[i].defaultValue = '报告_' + new Date().getTime();
}
}
formatDataFun(tableData.value, 0);
nextTick(() => {
baseTableRef.value.setDataFun(tableData.value);
});
setTimeout(() => {
baseTableRef.value.tableRef.setAllTreeExpand(true);
}, 200);
}
},
{
immediate: true,
deep: true,
}
);
</script>
<style lang="scss" scoped>
.flow-node-param-page {
width: 100%;
height: 100%;
.span-fommat {
max-width: 50px;
text-align: right;
flex-shrink: 0;
}
.span-min80 {
min-width: 80px;
}
.span-3 {
width: calc(100% / 3);
}
.spanw100 {
width: 100px;
}
.table-box {
width: 100%;
// height: calc(100% - 50px);
height: 100%;
}
.operate-box {
width: 100%;
height: 50px;
display: flex;
align-items: center;
justify-content: flex-end;
}
}
.slider {
display: flex;
align-items: center;
height: 40px;
padding: 0 12px;
}
.value {
padding-right: 15px;
min-width: 40px;
}
.mr10 {
margin-right: 10px;
}
.ml10 {
margin-left: 10px;
}
.w70p{
width: 60%;
}
.input-grid {
display: flex;
align-items: center;
}
.required-icon {
color: red;
margin-right: 2px;
}
</style>