CMVR-BID/src/views/flow/nodes/function/loop.vue

317 lines
8.7 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="group__container" :class="props.model.id" @mouseleave="setNodeProperties">
<NodeTitle
:icon="loop"
:nodeId="props.model.id"
:nodeProperties="props.properties"
:nodeName="props.properties?.name || '循环'"
nodeDesc="循环执行一系列任务,直至输出所有结果"
:showZoom="false"
@setNodeName="setNodeName"
/>
<div class="loop__container">
<div>
<el-form
:inline="true"
:model="formData"
:rules="rules"
ref="dynamicForm"
label-position="top"
label-width="auto"
:disabled="flowStore.disableForm"
>
<div v-for="(property, index) in formData.nodeParams" :key="index">
<el-row>
<el-form-item
:label="index === 0 ? '参数名' : ''"
:prop="`nodeParams.${index}.name`"
:rules="[
{
required: true,
message: '请输入参数名',
trigger: 'blur',
},
]"
>
<el-input
:disabled="index === 0"
v-model="property.name"
placeholder="请输入"
clearable
/>
</el-form-item>
<el-form-item
:label="index === 0 ? '参数值' : ''"
:prop="`nodeParams.${index}.type`"
>
<el-select
v-model="property.type"
@change="handleTypeChange(index)"
>
<el-option label="引用" value="quote" />
<el-option label="输入" value="input" />
</el-select>
<el-form-item
v-if="property.type === 'input'"
:rules="[
{
required: true,
message: '请输入参数值',
trigger: 'blur',
},
]"
:prop="`nodeParams.${index}.input`"
>
<el-input-number
v-model="property.input"
:min="0"
:controls="false"
:step-strictly="true"
placeholder="请输入"
clearable
/>
</el-form-item>
<el-form-item
v-if="property.type === 'quote'"
:rules="[
{
required: true,
message: '请选择参数值',
trigger: 'blur',
},
]"
:prop="`nodeParams.${index}.quote`"
>
<el-cascader
:ref="
(el) => {
if (el) cascaderRefs[index] = el;
}
"
v-model="property.quote"
:checkStrictly="true"
:options="quoteOptions"
placeholder="请选择"
@visible-change="
(visible) =>
visibleChange(visible, index, property.quote)
"
@change="(value) => cascaderChange(value, index)"
/>
</el-form-item>
</el-form-item>
</el-row>
</div>
</el-form>
</div>
</div>
<div>循环体</div>
<div
class="child__container"
@drop="handleDrop"
@dragover="handleDragover"
>
<slot></slot>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from "vue";
import NodeTitle from "../../components/NodeTitle.vue";
import loop from "../../icon/loop.svg";
import { useFlowStore } from "@/store/modules/flow";
import { addNewEdge, getInput } from "@/utils/flow";
const props = defineProps({
model: Object,
graphModel: Object,
properties: Object,
nowTime: Number,
});
const flowStore = useFlowStore();
const formData = reactive({
nodeParams: [{ name: "loopNum", type: "input", input: null, quote: "" }],
});
const quoteOptions = ref([]);
const handleTypeChange = (index) => {
if (formData.nodeParams[index].type === "input") {
formData.nodeParams[index].quote = "";
} else {
formData.nodeParams[index].input = null;
const option = getInput(props.model.id);
if (option) {
quoteOptions.value = option;
}
}
};
const rules = reactive({});
const emits = defineEmits(["bindRef", "addToGroup", "contentChange"]);
const handleDragover = (e) => {
e.preventDefault();
};
const handleDrop = (e) => {
e.preventDefault();
e.stopPropagation();
let flowNode = e.dataTransfer.getData("flowNode");
flowNode = JSON.parse(flowNode);
const point = lf.getPointByClient(e.clientX, e.clientY);
const { type, ...other } = flowNode;
const node = lf.addNode({
type,
x: point.canvasOverlayPosition.x,
y: point.canvasOverlayPosition.y,
properties: {
parentId: props.model.id,
...other,
},
});
emits("addToGroup", node.id);
setTimeout(() => {
addNewEdge(props.model.id);
}, 50);
};
const dynamicForm = ref();
const setNodeProperties = async () => {
try {
const valid = await dynamicForm.value.validate();
if (valid) {
const data = toRaw(formData);
setTimeout(() => {
const properties = lf.getProperties(props.model.id);
window.lf.setProperties(props.model.id, {
...properties,
...data,
});
}, 50);
}
} catch (error) {
dynamicForm.value.clearValidate();
}
};
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
...properties,
name
});
emits("contentChange");
}
const visibleChange = async (value, index, quote) => {
if (value) {
const option = getInput(props.model.id);
if (option) {
quoteOptions.value = option;
const currentValue = [...quote];
// 强制级联选择器重新处理选中值与选项的匹配
if (cascaderRefs.value[index]) {
// 等待DOM更新后再设置值,确保新options已生效
setTimeout(() => {
// 如果当前有选中值,重新设置一次以触发重新匹配
if (currentValue.length) {
formData.nodeParams[index].quote = [];
// 确保响应式更新
setTimeout(() => {
console.log(1247);
formData.nodeParams[index].quote = currentValue;
}, 0);
}
// 手动触发级联选择器的重新渲染
// cascaderRefs.value[index].updatePopper();
}, 0);
}
}
}
};
const cascaderRefs = ref([]);
const cascaderChange = (value, index) => {
const selectedOptions = cascaderRefs.value[index].getCheckedNodes(true);
formData.nodeParams[index].quote = value;
formData.nodeParams[index].quoteType = selectedOptions[0].data.type;
};
watch(
() => props.properties,
() => {
if (props.properties.nodeParams && props.properties.nodeParams.length > 0) {
formData.nodeParams = props.properties.nodeParams;
const option = getInput(props.model.id);
if (option) {
quoteOptions.value = option;
}
}
},
{
immediate: true,
deep: true,
}
);
onMounted(() => {
emits("bindRef", dynamicForm.value);
});
</script>
<style lang="scss" scoped>
.group__container {
width: 100%;
height: 100%;
min-height: 420px;
background: #fff;
padding: 12px;
cursor: default;
border-radius: 12px;
border: 2px solid white;
box-shadow: 0 5px 15px 0#00000008;
display: flex;
flex-direction: column;
position: relative;
.title {
height: 40px;
}
:deep(.loop__container) {
height: auto;
.el-input {
width: 240px;
}
.el-input-number {
width: 92px;
}
.el-select {
width: 120px;
}
.el-cascader {
width: 240px;
}
}
.child__container {
min-height: 100px;
flex: 1;
background-color: #f6f8fa;
border-radius: 8px;
}
.el-row {
align-items: end;
}
}
</style>