This commit is contained in:
2025-12-01 15:13:10 +08:00
14 changed files with 242 additions and 57 deletions

View File

Before

Width:  |  Height:  |  Size: 698 B

After

Width:  |  Height:  |  Size: 698 B

View File

Before

Width:  |  Height:  |  Size: 758 B

After

Width:  |  Height:  |  Size: 758 B

View File

@@ -423,6 +423,9 @@ const deleteTableRowFun = async(row: any) => {
// });
};
const actionList = computed(() => {
if (props.readonly) {
return [];
}
if (!props.hasOperationColumn) {
return [];
}

View File

@@ -1,3 +1,22 @@
export enum FLOW_APP_TYPE {
/**
* 本地应用
*/
LOCAL = '1',
/**
* 云应用
*/
CLOUD = '2',
/**
* HPC
*/
HPC = '3',
/**
* 脚本节点
*/
SCRIPT_NODE = '5'
}
export enum FLOW_USE_STATUS {
/**
* 禁用

View File

@@ -1,4 +1,4 @@
import { FLOW_NODE_TYPE_ENUM } from '@/utils/enum/flow';
import { FLOW_APP_TYPE, FLOW_NODE_TYPE_ENUM } from '@/utils/enum/flow';
import { ElMessage } from 'element-plus';
import { useNodeAttribute } from './nodeEvents';
@@ -20,7 +20,7 @@ export const getRelationNodeAndVerifyFlowFun = (cells:any, templateName:string,
const nodeArr = [];
const edgeArr = [];
for (let index = 0; index < cells.length; index++) {
if (cells[index].shape === 'edge') {
if (cells[index].shape === 'edge' || cells[index].shape === 'agent-edge') {
edgeArr.push({
target: cells[index].target.cell,
source: cells[index].source.cell,
@@ -64,7 +64,7 @@ const sortNodeAndEdge = (nodeArr: any, edgeArr: any) => {
console.log('生成一个树结构的节点数据');
getNodeRelation(startNode, [startNode.id], nodeArr, edgeArr);
const relationNode = startNode;
// console.log('relationNode', startNode);
console.log('relationNode', startNode);
const repeatNodeObj:any = {};
for (let index = 0; index < edgeArr.length; index++) {
@@ -202,7 +202,8 @@ const formatNodeAndEdge = (edgeArr:any[], nodeArr:any[]) => {
});
nodeArr.forEach((node:any) => {
const formatItem:any = {
id: node.id, type: node.type,
id: node.id,
type: node.type,
nodeType: node.nodeType,
name: node.label,
};
@@ -227,49 +228,65 @@ const formatNodeAndEdge = (edgeArr:any[], nodeArr:any[]) => {
postScripts: node.postScripts,
preScripts: node.preScripts,
uuid: node.uuid,
exeMethod: node.exeMethod,
};
if (node.type === '本地应用') {
if (node.nodeTypeValue === FLOW_APP_TYPE.LOCAL) {
formatItem['extensionElements']['executeConfig'].type = 'localApp';
formatItem['extensionElements']['executeConfig'].executeType = 'localApp';
}
if (node.type === '云应用') {
if (node.nodeTypeValue === FLOW_APP_TYPE.CLOUD) {
formatItem['extensionElements']['executeConfig'].type = 'cloudApp';
formatItem['extensionElements']['executeConfig'].executeType = 'cloudApp';
}
if (node.type === 'HPC') {
if (node.nodeTypeValue === FLOW_APP_TYPE.HPC) {
formatItem['extensionElements']['executeConfig'].type = 'HPC';
formatItem['extensionElements']['executeConfig'].executeType = 'HPC';
formatItem['extensionElements']['executeConfig'].waitUser = node.exeMethod !== 'serviceTask';
formatItem['extensionElements']['executeConfig'].asyncCallback = true;
} else {
formatItem['extensionElements']['executeConfig'].asyncCallback = false;
}
if (node.type === '脚本节点') {
if (node.nodeTypeValue === FLOW_APP_TYPE.SCRIPT_NODE) {
formatItem['extensionElements']['executeConfig'].type = 'scriptNode';
formatItem['extensionElements']['executeConfig'].executeType = 'scriptNode';
formatItem['extensionElements']['executeConfig'].executeType = 'exportWordScript';
}
if (node.nodeTypeValue === FLOW_APP_TYPE.HPC) {
// HPC节点异步回调为true
// formatItem.asyncCallback = true;
// formatItem.waitUser = node.exeMethod === 'serviceTask';
// HPC节点type为serviceTask
formatItem.type = 'serviceTask';
} else {
// 非HPC节点异步回调为false
// formatItem.asyncCallback = false;
// 非HPC节点type为userTask
formatItem.type = 'userTask';
}
if (node.nodeTypeValue === FLOW_APP_TYPE.SCRIPT_NODE) {
formatItem.type = 'serviceTask';
}
delete formatItem['extensionElements']['executeConfig']['iconUrl'];
delete formatItem['extensionElements']['executeConfig']['children'];
delete formatItem['extensionElements']['executeConfig']['children'];
if (formatItem.nodeType === FLOW_NODE_TYPE_ENUM.START) {
formatItem.type = 'startEvent';
delete formatItem['extensionElements'];
}
if (formatItem.nodeType === FLOW_NODE_TYPE_ENUM.END) {
formatItem.type = 'endEvent';
delete formatItem['extensionElements'];
}
delete formatItem.iconUrl;
delete formatItem.pageConfigList;
delete formatItem.children;
delete formatItem.preNode;
delete formatItem.nodeType;
if (formatItem.nodeType === FLOW_NODE_TYPE_ENUM.START) {
formatItem.type = 'startEvent';
}
if (formatItem.nodeType === FLOW_NODE_TYPE_ENUM.END) {
formatItem.type = 'endEvent';
}
if (node.exeMethod === 'userTask') {
formatItem.type = 'userTask';
}
else if (node.exeMethod === 'serviceTask') {
formatItem.type = 'serviceTask';
} else {
// 默认自动执行
formatItem.type = 'serviceTask';
}
nodeFormatArr.push(formatItem);
});
return [...nodeFormatArr, ...edgeFormatArr];

View File

@@ -7,6 +7,7 @@ import { History } from '@antv/x6-plugin-history';
import { keyboardEvents } from './keyboard';
import { stencilRegister } from './stencil';
import { initNodeEvents } from './nodeEvents';
import { v4 as uuidv4 } from 'uuid';
export const FLOW_CREATE_ID = 'flow-create-content';
@@ -50,7 +51,7 @@ export const initGraph = async () => {
allowLoop: false,
highlight: true,
createEdge(): Edge {
return graph.createEdge({ shape: 'agent-edge' });
return graph.createEdge({ shape: 'agent-edge', id: 'uuid-edge-' + uuidv4() });
},
validateConnection({ targetMagnet }) {
return !!targetMagnet;

View File

@@ -1,5 +1,6 @@
import { reactive, ref } from 'vue';
import { FLOW_CREATE_ID } from './initGraph';
import { ElMessage } from 'element-plus';
// 控制连接桩显示/隐藏
const showPorts = (ports: NodeListOf<SVGElement>, show: boolean) => {
@@ -77,6 +78,7 @@ export const initNodeEvents = (graph: any) => {
export const useNodeEvents = () => {
return {
undoFun,
deleteFun,
zoomInFun,
zoomOutFun,
yAlign,
@@ -89,6 +91,11 @@ const undoFun = (graph: any) => {
graph.undo();
};
const deleteFun = (graph: any) => {
const nodes = graph.getSelectedCells();
graph.removeCells(nodes);
};
const zoomInFun = (graph: any) => {
graph.zoom(-0.2);
};
@@ -97,29 +104,59 @@ const zoomOutFun = (graph: any) => {
graph.zoom(0.2);
};
/**
* 全部左对齐
* @param graph
*/
// const xAlign = (graph: any) => {
// const nodes = graph.getSelectedCells();
// let x = 0;
// for (let index = 0; index < nodes.length; index++) {
// if (x === 0 || x > nodes[index].position().x) {
// x = nodes[index].position().x;
// }
// }
// for (let index = 0; index < nodes.length; index++) {
// nodes[index].position(x, nodes[index].position().y);
// }
// };
/**
* 横向分布
* @param graph
*/
const xAlign = (graph: any) => {
const nodes = graph.getSelectedCells();
let x = 0;
for (let index = 0; index < nodes.length; index++) {
if (x === 0 || x > nodes[index].position().x) {
x = nodes[index].position().x;
if (nodes.length > 2) {
nodes.sort((a:any, b:any) => {
return a.position().x - b.position().x;
});
const xLength = nodes[1].position().x - nodes[0].position().x - nodes[0].size().width;
for (let index = 2; index < nodes.length; index++) {
nodes[index].position(nodes[index - 1].position().x + nodes[index - 1].size().width + xLength, nodes[index].position().y);
}
} else {
ElMessage.warning('请框选超过 2 个节点!');
}
for (let index = 0; index < nodes.length; index++) {
nodes[index].position(x, nodes[index].position().y);
}
// for (let index = 0; index < nodes.length; index++) {
// nodes[index].position(x, nodes[index].position().y);
// }
};
const yAlign = (graph: any) => {
const nodes = graph.getSelectedCells();
let y = 0;
for (let index = 0; index < nodes.length; index++) {
if (y === 0 || y > nodes[index].position().y) {
y = nodes[index].position().y;
if (nodes.length > 1) {
let y = 0;
for (let index = 0; index < nodes.length; index++) {
if (y === 0 || y > nodes[index].position().y) {
y = nodes[index].position().y;
}
}
}
for (let index = 0; index < nodes.length; index++) {
nodes[index].position(nodes[index].position().x, y);
for (let index = 0; index < nodes.length; index++) {
nodes[index].position(nodes[index].position().x, y);
}
} else {
ElMessage.warning('请框选超过 1 个节点!');
}
};

View File

@@ -3,7 +3,7 @@ import { defineComponent, useAttrs, watch } from 'vue';
import Render from './render.vue';
import { CopyDocument, Delete } from '@element-plus/icons-vue';
import Draggable from 'vuedraggable';
import deleteIcon from '@/assets/imgs/dragFlow/delete.svg';
import deleteIcon from '@/assets/imgs/dragFlow/delete-red.svg';
import editIcon from '@/assets/imgs/dragFlow/edit.svg';
const Layouts = defineComponent({

View File

@@ -1026,7 +1026,7 @@ const changeLabel = (activeData: any) => {
.el-date-editor {
width: 227px;
}
::v-deep .el-icon-time {
.el-icon-time {
display: none;
}
}

View File

@@ -9,6 +9,7 @@ import stencilStartImg from '@/assets/imgs/dragFlow/dragIcon/stencil_start.svg';
import stencilEndImg from '@/assets/imgs/dragFlow/dragIcon/stencil_end.svg';
import { FLOW_CREATE_ID } from './initGraph';
import { FLOW_NODE_TYPE_ENUM } from '@/utils/enum/flow';
import { v4 as uuidv4 } from 'uuid';
const commonStore = CommonStore();
@@ -81,6 +82,7 @@ export const stencilRegister = async (graph: any) => {
stencil.dnd.options.getDropNode = (node: any) => {
console.log('node', node);
return graph.createNode({
id: 'uuid-node-' + uuidv4(),
shape: node.data.isApp ? 'custom-app-vue-node' : 'custom-local-vue-node',
// shape: 'custom-app-vue-node',
data: { ...node.data },
@@ -110,6 +112,7 @@ export const createNode = (nodeListObj:any, graph:any, stencil?:any) => {
uuid: item.isLocal ? '' : item.uuid,
type: item.isLocal ? nodeListObj[key].type : nodeListObj[key].label,
nodeType: item.isLocal ? item.nodeType : nodeListObj[key].label,
nodeTypeValue: nodeListObj[key].type,
iconUrl: item.isLocal ? item.appImage : FileUtil.getFilePreviewImgPathUrl(item.appImage),
preScripts: [],
postScripts: [],

View File

@@ -8,6 +8,9 @@
.no-padding {
padding: 0 !important;
}
#delete-btn {
width: 16px;
}
.header-box {
display: flex;

View File

@@ -23,11 +23,42 @@
</el-select>
</div>
<div class="center-box">
<img @click="undoFun(graph)" src="@/assets/imgs/dragFlow/undo.svg" alt="">
<img @click="zoomOutFun(graph)" src="@/assets/imgs/dragFlow/magnify.svg" alt="">
<img @click="zoomInFun(graph)" src="@/assets/imgs/dragFlow/lessen.svg" alt="">
<img title="纵向对齐" @click="yAlign(graph)" src="@/assets/imgs/dragFlow/vertical-center.svg" alt="">
<img title="横向对齐" @click="xAlign(graph)" src="@/assets/imgs/dragFlow/lateral-distribution.svg" alt="">
<el-tooltip
effect="dark"
content="上一步"
>
<img @click="undoFun(graph)" src="@/assets/imgs/dragFlow/undo.svg" alt="">
</el-tooltip>
<el-tooltip
effect="dark"
content="放大画布"
>
<img @click="zoomOutFun(graph)" src="@/assets/imgs/dragFlow/magnify.svg" alt="">
</el-tooltip>
<el-tooltip
effect="dark"
content="缩小画布"
>
<img @click="zoomInFun(graph)" src="@/assets/imgs/dragFlow/lessen.svg" alt="">
</el-tooltip>
<el-tooltip
effect="dark"
content="框选节点垂直居中"
>
<img @click="yAlign(graph)" src="@/assets/imgs/dragFlow/vertical-center.svg" alt="">
</el-tooltip>
<el-tooltip
effect="dark"
content="框选节点横向分布"
>
<img @click="xAlign(graph)" src="@/assets/imgs/dragFlow/lateral-distribution.svg" alt="">
</el-tooltip>
<el-tooltip
effect="dark"
content="框选节点删除"
>
<img id="delete-btn" @click="deleteFun(graph)" src="@/assets/imgs/dragFlow/delete-black.svg" alt="">
</el-tooltip>
</div>
<div class="right-box">
<el-button v-if="operationType !== FLOW_OPERATION_TYPE.UPGRADE" @click="saveDraftFun()">保存草稿</el-button>
@@ -94,7 +125,7 @@ const currentVersion = ref<any>();
const flowVersionOptions = ref<any[]>([]);
const { drawerVisible, nodeAttribute } = useNodeAttribute();
const { undoFun, zoomInFun, zoomOutFun, xAlign, yAlign } = useNodeEvents();
const { undoFun, deleteFun, zoomInFun, zoomOutFun, xAlign, yAlign } = useNodeEvents();
// 注册连线
// registerCustomConnector();

View File

@@ -1,6 +1,6 @@
.task-content {
// height: 100%;
height: 800px;
height: 70vh;
width: 100%;
.select-method {
height: 32px;

View File

@@ -3,7 +3,7 @@
v-model="dialogVisible"
diaTitle="任务创建"
width="90%"
height="800"
height="600"
:loading="loadingInterface"
@close="closeFun"
show-footer
@@ -19,7 +19,17 @@
<div class="left-panel" v-if="selectMethod === 'template'">
<div class="top-bar">
<el-form :inline="true" class="left-form-inline">
<el-form-item label="工况清单库">
<el-form-item label="策划模式">
<el-select
class="loadcase-lib"
:teleported="false"
v-model='planMode'
:fit-input-width="true"
>
<el-option v-for="item in planModeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="工况清单库:" v-show="planMode === 'lib'">
<el-select
class="loadcase-lib"
:teleported="false"
@@ -32,7 +42,7 @@
<el-option v-for="item in loadcaseLibList" :key="item.value" :label="item.poolName" :value="item" />
</el-select>
</el-form-item>
<el-form-item label="版本:">
<el-form-item label="版本:" v-show="planMode === 'lib'">
<el-select
class="version"
:teleported="false"
@@ -45,10 +55,23 @@
<el-option v-for="item in loadcaseLibVersionList" :key="item.value" :label="item.poolVersion" :value="item" />
</el-select>
</el-form-item>
<el-form-item label="表单文件:" v-show="planMode === 'excel'">
<el-upload
class="upload"
:limit="1"
accept=".xls,.xlsx"
:auto-upload="false"
:show-file-list="false"
:on-change="uploadExcelFileFun"
>
<el-button type="primary">{{ $t('知识库.上传') }}</el-button>
</el-upload>
</el-form-item>
</el-form>
</div>
<div class="left-table">
<loadCaseTable
v-show="planMode === 'lib'"
ref="leftPoolTableRef"
:tableName="TABLE_NAME.TASK_POOL"
:data="leftPoolTableDataList"
@@ -56,6 +79,15 @@
:loading="leftTableLoading"
>
</loadCaseTable>
<loadCaseTable
v-show="planMode === 'excel'"
ref="leftExcelTableRef"
:tableName="TABLE_NAME.TASK_POOL"
:data="leftExcelTableData"
:loading="leftExcelLoading"
readonly
>
</loadCaseTable>
</div>
</div>
<!-- 左侧项目列表 -->
@@ -171,7 +203,7 @@ import { canAddChild, transformPoolNodesToTree } from '@/utils/node';
import { ElMessage, ElMessageBox } from 'element-plus';
import { cloneDeep } from 'lodash-es';
import { v4 as uuidv4 } from 'uuid';
import { getAllTaskPoolApi, getTaskPoolApi, getTaskPoolVersionsApi } from '@/api/task/taskpool';
import { getAllTaskPoolApi, getTaskPoolApi, getTaskPoolVersionsApi, importTaskPoolApi } from '@/api/task/taskpool';
import { NODE_TYPE, getTagKeyMap, tagSortList } from '@/utils/enum/node';
import nodeLevel2Select from '@/components/project/nodeLevel2Select.vue';
import { getTaskTreeFun } from '../../projectDetail/components/projectApi';
@@ -185,6 +217,7 @@ import { getMemberListIds } from '@/utils/task';
import { getUserTenantId } from '@/utils/user';
import ProjectSelect from '@/components/common/projectSelect/index.vue';
import { TABLE_NAME } from '@/utils/enum/tableName';
import { taskPoolDictOptions } from '@/utils/project';
const props = defineProps<{
showTaskDialog: boolean;
@@ -207,7 +240,11 @@ const getRightVxeRef = () => {
const getLeftVxeRef = () => {
if (selectMethod.value === 'template') {
return leftPoolTableRef?.value?.loadcaseTableRef?.TreeTableRef?.treeTableRef;
if (planMode.value === 'lib') {
return leftPoolTableRef?.value?.loadcaseTableRef?.TreeTableRef?.treeTableRef;
} else {
return leftExcelTableRef?.value?.loadcaseTableRef?.TreeTableRef?.treeTableRef;
}
} else {
return leftProjectTableRef?.value?.loadcaseTableRef?.TreeTableRef?.treeTableRef;
}
@@ -771,10 +808,8 @@ const queryTaskPoolFun = async () => {
version: currentLoadcaseLibVersionInfo.value.poolVersion,
};
const res: any = await getTaskPoolApi(req);
let tree = [];
if (res.code === 200 && res.data.poolBrief && res.data && res.data.nodes) {
tree = transformPoolNodesToTree(res.data.nodes);
leftPoolTableDataList.value = tree;
leftPoolTableDataList.value = transformPoolNodesToTree(res.data.nodes);
nextTick(() => {
// 待修改
// leftPoolTableRef.value?.changeLevel('全部展开');
@@ -785,6 +820,42 @@ const queryTaskPoolFun = async () => {
leftTableLoading.value = false;
};
/**
* @lib lib 工况库导入
* @excel excel 外部表单导入
*/
const planMode = ref('lib');
const planModeOptions = [{ label: '工况库导入', value: 'lib' }, { label: '外部表单导入', value: 'excel' }];
const leftExcelTableData = ref();
const leftExcelLoading = ref(false);
const leftExcelTableRef = ref();
const uploadExcelFileFun = async(file:any) => {
console.log('file', file);
leftExcelLoading.value = true;
const dict = {
bCapacity: taskPoolDictOptions.value.capacityOptions,
unit: taskPoolDictOptions.value.performanceUnitOptions,
department: taskPoolDictOptions.value.departmentListOptions,
section: taskPoolDictOptions.value.sectionListOptions,
group: taskPoolDictOptions.value.groupListOptions,
};
const req = {
poolName: '外部临时表单',
file: file.raw,
dicts: JSON.stringify(dict),
};
const res: any = await importTaskPoolApi(req);
if (res.code === 200) {
leftExcelTableData.value = transformPoolNodesToTree(res.data.nodes);
}
leftExcelLoading.value = false;
};
setTimeout(() => {
// rightTableData.value = [
// {