CMVR-IOT-UI/src/views/flow/nodes/common/startNode.vue
2025-08-21 14:11:58 +08:00

368 lines
8.0 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="node__container">
<keep-alive>
<NodeState :state="nodeOperatingStatus">
<template #input>
<JsonViewer :value="inputJsonData" copyable boxed sort theme="light" />
</template>
<!-- <template #output>
<JsonViewer :value="outputJsonData" copyable boxed sort theme="light" />
</template> -->
</NodeState>
</keep-alive>
<div class="title__container">
<div class="title">
<img src="../../icon/start.svg" alt="" />
<span class="text">Start</span>
</div>
<div class="subTitle">工作流的起始节点,用于设定启动工作流需要的信息</div>
</div>
<div class="input__container">
<div class="title">
<div class="left">
<div class="tag"></div>
<div class="text">输入</div>
</div>
<div class="right">
<el-button
:disabled="flowStore.disableForm"
@click="addFormItem"
class="addFormItem"
>
<el-icon :size="20"><Plus /></el-icon>
</el-button>
</div>
</div>
<div class="form__container">
<el-form
:inline="true"
:model="formData"
:rules="rules"
ref="dynamicForm"
label-position="top"
label-width="auto"
:disabled="flowStore.disableForm"
>
<FormItemRecursive
:current-list="formData.inputParams"
prop-path="inputParams"
:depth="0"
:is-first-level="true"
:endDepth="2"
:parent-path="[]"
@delete-item="deleteTopLevelItem"
/>
</el-form>
</div>
</div>
</div>
</template>
<script setup>
import { watch, reactive, toRaw, ref, onMounted, onUnmounted } from "vue";
import { Plus } from "@element-plus/icons-vue";
import { useFlowStore } from "@/store/modules/flow";
import NodeState from "../../components/NodeState.vue";
import "vue3-json-viewer/dist/index.css";
import { emitter } from "@/utils/eventBus";
import FormItemRecursive from "./FormItemRecursive.vue";
const props = defineProps({
model: Object,
properties: Object,
});
const emits = defineEmits(["contentChange"]);
const flowStore = useFlowStore();
const formData = reactive({
inputParams: [
{
name: "terminalId",
type: "string",
desc: "机器人ip",
required: true,
children: [],
},
],
});
const rules = reactive({});
const addFormItem = () => {
formData.inputParams.push({
name: "",
type: "",
desc: "",
required: false,
children: [],
});
emits("contentChange");
};
const dynamicForm = ref();
const setNodeProperties = async () => {
try {
const result = await dynamicForm.value.validate();
if (result) {
const data = toRaw(formData);
lf.setProperties(props.model.id, {
...props.properties,
...data,
});
emits("contentChange");
}
} catch {
dynamicForm.value.clearValidate()
}
};
const nodeOperatingStatus = ref("NORMAL");
const inputJsonData = ref({});
const outputJsonData = ref({});
watch(
() => props.properties,
() => {
if (props.properties.inputParams && props.properties.inputParams.length > 0) {
formData.inputParams = props.properties.inputParams;
}
},
{
immediate: true,
deep: true,
}
);
// 删除顶层表单项
const deleteTopLevelItem = (fullPath) => {
// 从顶层数据开始查找
let currentLevel = formData.inputParams;
// 遍历路径(除最后一个索引,因为最后一个是要删除的项)
for (let i = 0; i < fullPath.length - 1; i++) {
const index = fullPath[i];
// 进入下一层级
currentLevel = currentLevel[index].children;
}
// 最后一个索引是当前层级要删除的项
const lastIndex = fullPath[fullPath.length - 1];
currentLevel.splice(lastIndex, 1);
emits("contentChange");
}
onMounted(() => {
setNodeProperties();
emitter.on("changeNodeState", (data) => {
if (data.nodeId === props.model.id) {
nodeOperatingStatus.value = data.status;
let inputData = {};
let output = {};
try {
inputData = JSON.parse(data.paramsIn) || {};
output = JSON.parse(data.paramsOut) || {};
} catch (error) {
inputData = data.paramsIn || {};
output = data.paramsOut || {};
}
inputJsonData.value = inputData;
outputJsonData.value = output;
emits("contentChange");
}
});
emitter.on("contentChange", (data) => {
if (data.id === props.model.id) {
nodeOperatingStatus.value = "NORMAL";
inputJsonData.value = {};
outputJsonData.value = {};
emits("contentChange");
}
});
});
const validateForm = async () => {
try {
const result = await dynamicForm.value.validate();
if (result) {
return { valid: true, message: "验证通过" };
} else {
return { valid: false, message: "验证失败" };
}
} catch {
return { valid: false, message: "验证失败" };
}
};
onUnmounted(() => {
emitter.off("changeNodeState");
emitter.off("contentChange");
});
defineExpose({
validateForm,
setNodeProperties
});
</script>
<style lang="scss" scoped>
.node__container {
width: 100%;
height: auto;
.title__container {
.title {
display: flex;
align-items: center;
img {
width: 24px;
height: 24px;
}
.text {
font-size: 16px;
font-weight: bold;
margin-left: 12px;
}
}
.subTitle {
color: #737a87;
font-size: 12px;
margin: 8px 0;
}
}
.input__container {
width: 100%;
background-color: #fafbfc;
padding: 0 16px;
border-radius: 8px;
box-sizing: border-box;
.title {
height: 32px;
display: flex;
align-items: center;
justify-content: space-between;
.left {
display: flex;
align-items: center;
.tag {
width: 3px;
height: 16px;
background: #1664ff;
border-radius: 0 4px 4px 0;
margin-right: 12px;
}
.text {
font-size: 16px;
font-weight: 700;
color: #0c0d0e;
}
}
.right {
.addFormItem {
border: none;
background: transparent;
cursor: pointer;
}
}
}
.form__container {
margin: 12px 0;
.sub-properties {
margin-left: 10px;
.zw {
margin-left: 15px;
&::before {
content: "";
position: absolute;
left: 0;
top: -4px;
width: 10px;
border-left: 1px solid gray;
border-bottom: 1px solid gray;
border-bottom-left-radius: 4px;
background-color: transparent;
height: 24px;
}
}
}
.gSon-properties {
margin-left: 10px;
.zw {
margin-left: 15px;
&::before {
content: "";
position: absolute;
left: 0;
top: -4px;
width: 10px;
border-left: 1px solid gray;
border-bottom: 1px solid gray;
border-bottom-left-radius: 4px;
background-color: transparent;
height: 24px;
}
}
}
.deleteBtn {
margin-bottom: 22px;
cursor: pointer;
}
.addBtn {
margin-bottom: 22px;
cursor: pointer;
}
:deep(.el-row) {
align-items: end;
.el-input {
--el-input-width: 120px;
}
.el-select {
--el-select-width: 120px;
}
.el-cascader {
--el-form-inline-content-width: 120px;
}
}
}
}
}
</style>
<style lang="scss">
.popover__container {
min-width: 300px !important;
.params__container {
margin-bottom: 12px;
.paramsTitle {
color: #0c0d0e;
font-size: 14px;
font-weight: 500;
}
}
}
</style>