feat: http、loop、sleep节点重构
This commit is contained in:
parent
090183a287
commit
75f64e8d9f
@ -43,7 +43,7 @@
|
|||||||
</el-collapse>
|
</el-collapse>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="['currentLoop', 'serviceNode'].includes(props.data.type)">
|
<div v-if="['currentLoop', 'serviceNode', 'sleep', 'loop'].includes(props.data.type)">
|
||||||
<el-collapse v-model="activeNames">
|
<el-collapse v-model="activeNames">
|
||||||
<el-collapse-item name="1" icon-position="left">
|
<el-collapse-item name="1" icon-position="left">
|
||||||
<template #title>
|
<template #title>
|
||||||
@ -395,6 +395,494 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="props.data.type === 'http'">
|
||||||
|
<el-form
|
||||||
|
:inline="true"
|
||||||
|
:model="httpNodeData"
|
||||||
|
:rules="rules"
|
||||||
|
class="http-form"
|
||||||
|
ref="dynamicForm"
|
||||||
|
label-position="top"
|
||||||
|
label-width="auto"
|
||||||
|
>
|
||||||
|
<el-collapse v-model="activeNames">
|
||||||
|
<el-collapse-item name="1" icon-position="left">
|
||||||
|
<template #title>
|
||||||
|
请求配置
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="dark"
|
||||||
|
content="支持HTTP/HTTPS协议"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-icon class="header-icon" style="margin-left: 6px">
|
||||||
|
<info-filled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
<div class="form__container">
|
||||||
|
<div v-for="(property, index) in httpNodeData.config" :key="index">
|
||||||
|
<el-row>
|
||||||
|
<el-form-item
|
||||||
|
:label="index === 0 ? '参数名' : ''"
|
||||||
|
:prop="`config.${index}.name`"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入参数名', trigger: 'blur' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
:disabled="true"
|
||||||
|
class="param-name"
|
||||||
|
v-model="property.name"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:label="index === 0 ? '参数值' : ''"
|
||||||
|
:prop="`config.${index}.input`"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="property.type"
|
||||||
|
class="param-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: property?.required ?? true,
|
||||||
|
message: '请输入参数值',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:prop="`config.${index}.input`"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
class="param-value"
|
||||||
|
v-if="property.componentType === 'number'"
|
||||||
|
v-model="property.input"
|
||||||
|
:min="0"
|
||||||
|
:max="property.max || Infinity"
|
||||||
|
:controls="false"
|
||||||
|
:step-strictly="true"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<el-select
|
||||||
|
v-model="property.input"
|
||||||
|
class="param-value"
|
||||||
|
v-else-if="property.componentType === 'select'"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in property.selectOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-input
|
||||||
|
class="param-value"
|
||||||
|
v-else
|
||||||
|
v-model="property.input"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="property.type === 'quote'"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择参数值',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:prop="`config.${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>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="2" icon-position="left">
|
||||||
|
<template #title>
|
||||||
|
Header配置
|
||||||
|
<el-button
|
||||||
|
:circle="true"
|
||||||
|
size="small"
|
||||||
|
@click="(e) => addHttpNodeFormItem(e, 'headers')"
|
||||||
|
class="addFormItem"
|
||||||
|
>
|
||||||
|
<el-icon :size="14"><Plus /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<div class="form__container">
|
||||||
|
<div v-for="(property, index) in httpNodeData.headers" :key="index">
|
||||||
|
<el-row>
|
||||||
|
<el-form-item
|
||||||
|
:label="index === 0 ? '参数名' : ''"
|
||||||
|
:prop="`headers.${index}.name`"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入参数名', trigger: 'blur' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
class="param-name"
|
||||||
|
v-model="property.name"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:label="index === 0 ? '参数值' : ''"
|
||||||
|
:prop="`headers.${index}.input`"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="property.type"
|
||||||
|
class="param-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: property?.required ?? true,
|
||||||
|
message: '请输入参数值',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:prop="`headers.${index}.input`"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
class="param-value"
|
||||||
|
v-if="property.componentType === 'number'"
|
||||||
|
v-model="property.input"
|
||||||
|
:min="0"
|
||||||
|
:max="property.max || Infinity"
|
||||||
|
:controls="false"
|
||||||
|
:step-strictly="true"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<el-select
|
||||||
|
v-model="property.input"
|
||||||
|
class="param-value"
|
||||||
|
v-else-if="property.componentType === 'select'"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in property.selectOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-input
|
||||||
|
class="param-value"
|
||||||
|
v-else
|
||||||
|
v-model="property.input"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="property.type === 'quote'"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择参数值',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:prop="`headers.${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>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="3" icon-position="left">
|
||||||
|
<template #title>
|
||||||
|
Param配置
|
||||||
|
<el-button
|
||||||
|
:circle="true"
|
||||||
|
size="small"
|
||||||
|
@click="(e) => addHttpNodeFormItem(e, 'params')"
|
||||||
|
class="addFormItem"
|
||||||
|
>
|
||||||
|
<el-icon :size="14"><Plus /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<div class="form__container">
|
||||||
|
<div v-for="(property, index) in httpNodeData.params" :key="index">
|
||||||
|
<el-row>
|
||||||
|
<el-form-item
|
||||||
|
:label="index === 0 ? '参数名' : ''"
|
||||||
|
:prop="`params.${index}.name`"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入参数名', trigger: 'blur' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
class="param-name"
|
||||||
|
v-model="property.name"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:label="index === 0 ? '参数值' : ''"
|
||||||
|
:prop="`params.${index}.input`"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="property.type"
|
||||||
|
class="param-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: property?.required ?? true,
|
||||||
|
message: '请输入参数值',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:prop="`params.${index}.input`"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
class="param-value"
|
||||||
|
v-if="property.componentType === 'number'"
|
||||||
|
v-model="property.input"
|
||||||
|
:min="0"
|
||||||
|
:max="property.max || Infinity"
|
||||||
|
:controls="false"
|
||||||
|
:step-strictly="true"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<el-select
|
||||||
|
v-model="property.input"
|
||||||
|
class="param-value"
|
||||||
|
v-else-if="property.componentType === 'select'"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in property.selectOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-input
|
||||||
|
class="param-value"
|
||||||
|
v-else
|
||||||
|
v-model="property.input"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="property.type === 'quote'"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择参数值',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:prop="`params.${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>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="4" title="Body配置" icon-position="left">
|
||||||
|
<template #title>
|
||||||
|
Body配置
|
||||||
|
<el-button
|
||||||
|
:circle="true"
|
||||||
|
size="small"
|
||||||
|
@click="(e) => addHttpNodeFormItem(e, 'body')"
|
||||||
|
class="addFormItem"
|
||||||
|
>
|
||||||
|
<el-icon :size="14"><Plus /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<div class="form__container">
|
||||||
|
<el-select v-model="httpNodeData.body.type">
|
||||||
|
<el-option label="None" value="none" />
|
||||||
|
<el-option label="JSON" value="json" />
|
||||||
|
<el-option label="form-data" value="form-data" />
|
||||||
|
</el-select>
|
||||||
|
<div class="code__container" v-show="httpNodeData.body.type === 'json'">
|
||||||
|
<div ref="codeRef" style="width: 100%; height: 100%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="test" v-show="httpNodeData.body.type === 'form-data'">
|
||||||
|
<div v-for="(property, index) in httpNodeData.body.formData" :key="index">
|
||||||
|
<el-row>
|
||||||
|
<el-form-item
|
||||||
|
:label="index === 0 ? '参数名' : ''"
|
||||||
|
:prop="`params.${index}.name`"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入参数名', trigger: 'blur' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
class="param-name"
|
||||||
|
v-model="property.name"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:label="index === 0 ? '参数值' : ''"
|
||||||
|
:prop="`body.formData.${index}.input`"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="property.type"
|
||||||
|
class="param-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: property?.required ?? true,
|
||||||
|
message: '请输入参数值',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:prop="`body.formData.${index}.input`"
|
||||||
|
>
|
||||||
|
<el-input-number
|
||||||
|
class="param-value"
|
||||||
|
v-if="property.componentType === 'number'"
|
||||||
|
v-model="property.input"
|
||||||
|
:min="0"
|
||||||
|
:max="property.max || Infinity"
|
||||||
|
:controls="false"
|
||||||
|
:step-strictly="true"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<el-select
|
||||||
|
v-model="property.input"
|
||||||
|
class="param-value"
|
||||||
|
v-else-if="property.componentType === 'select'"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in property.selectOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-input
|
||||||
|
class="param-value"
|
||||||
|
v-else
|
||||||
|
v-model="property.input"
|
||||||
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="property.type === 'quote'"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择参数值',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:prop="`body.formData.${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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="handleClose">关闭</el-button>
|
<el-button @click="handleClose">关闭</el-button>
|
||||||
<el-button type="primary" @click="confirm">确定</el-button>
|
<el-button type="primary" @click="confirm">确定</el-button>
|
||||||
@ -430,19 +918,44 @@ const codeRef = ref()
|
|||||||
let editorInstance
|
let editorInstance
|
||||||
|
|
||||||
const handleClose = (done) => {
|
const handleClose = (done) => {
|
||||||
const filteredObj = {
|
if (props.data.type === 'http') {
|
||||||
inputParams: filterEmptyName(formData.inputParams),
|
const nodeParams = []
|
||||||
nodeParams: filterEmptyName(formData.nodeParams),
|
nodeParams.push({ name: "config", type: "input", input: "", children: httpNodeData.value.config, disabled: true, required: true })
|
||||||
outputParams: filterEmptyName(formData.outputParams),
|
if (httpNodeData.value.headers.length) {
|
||||||
};
|
nodeParams.push({ name: "headers", type: "input", input: "", children: httpNodeData.value.headers, disabled: true, required: true })
|
||||||
|
}
|
||||||
|
if (httpNodeData.value.params.length) {
|
||||||
|
nodeParams.push({ name: "params", type: "input", input: "", children: httpNodeData.value.params, disabled: true, required: true })
|
||||||
|
}
|
||||||
|
if (httpNodeData.value.body.type !== 'none') {
|
||||||
|
const bodyObj = {
|
||||||
|
name: "body", type: "input", input: "", children: [
|
||||||
|
{ name: 'bodyType', type: "input", input: "json", disabled: true },
|
||||||
|
{ name: 'json', type: "input", input: editorInstance.getValue() || '' },
|
||||||
|
{ name: 'formData', type: "input", input: "", children: httpNodeData.value.body.formData }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
nodeParams.push(bodyObj)
|
||||||
|
}
|
||||||
|
lf.setProperties(props.data.id, {
|
||||||
|
...props.data.properties,
|
||||||
|
nodeParams
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const filteredObj = {
|
||||||
|
inputParams: filterEmptyName(formData.inputParams),
|
||||||
|
nodeParams: filterEmptyName(formData.nodeParams),
|
||||||
|
outputParams: filterEmptyName(formData.outputParams),
|
||||||
|
};
|
||||||
|
|
||||||
|
lf.setProperties(props.data.id, {
|
||||||
|
...props.data.properties,
|
||||||
|
...filteredObj
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
lf.setProperties(props.data.id, {
|
|
||||||
...props.data.properties,
|
|
||||||
...filteredObj
|
|
||||||
});
|
|
||||||
emitter.emit("setProperties", { id: props.data.id});
|
emitter.emit("setProperties", { id: props.data.id});
|
||||||
|
|
||||||
|
|
||||||
emits('close')
|
emits('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,7 +1038,7 @@ const visibleChange = (value, index, quote) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeNames = ref(['1', '2'])
|
const activeNames = ref(['1', '2', '3', '4'])
|
||||||
|
|
||||||
function hasErrors() {
|
function hasErrors() {
|
||||||
const model = editorInstance.getModel();
|
const model = editorInstance.getModel();
|
||||||
@ -537,7 +1050,7 @@ function hasErrors() {
|
|||||||
return markers.some(marker => marker.severity === monaco.MarkerSeverity.Error);
|
return markers.some(marker => marker.severity === monaco.MarkerSeverity.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const initEditor = () => {
|
const initEditor = (type) => {
|
||||||
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
|
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
|
||||||
target: monaco.languages.typescript.ScriptTarget.ES2020,
|
target: monaco.languages.typescript.ScriptTarget.ES2020,
|
||||||
allowNonTsExtensions: true, // 允许非 ts 扩展名(.js)
|
allowNonTsExtensions: true, // 允许非 ts 扩展名(.js)
|
||||||
@ -554,19 +1067,9 @@ const initEditor = () => {
|
|||||||
noSyntaxValidation: false, // 开启语法检查
|
noSyntaxValidation: false, // 开启语法检查
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
editorInstance = monaco.editor.create(codeRef.value, {
|
editorInstance = monaco.editor.create(codeRef.value, {
|
||||||
value: [`
|
value: ['{}'].join('\n'),
|
||||||
// 方法定义不能修改
|
language: type === 'code' ? 'javascript' : 'json',
|
||||||
function handler(params) {
|
|
||||||
// 返回值是一个可序列化成 json 的 dict 或 object
|
|
||||||
const result ={
|
|
||||||
type: 2,
|
|
||||||
message: params.input
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}`].join('\n'),
|
|
||||||
language: 'javascript',
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
lineNumbers: 'on',
|
lineNumbers: 'on',
|
||||||
roundedSelection: true,
|
roundedSelection: true,
|
||||||
@ -604,9 +1107,9 @@ const disposeEditor = () => {
|
|||||||
watch(
|
watch(
|
||||||
() => props.drawer,
|
() => props.drawer,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal && props.data.type === 'code') {
|
if (newVal && ['code', 'http'].includes(props.data.type)) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
initEditor();
|
initEditor(props.data.type);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
disposeEditor();
|
disposeEditor();
|
||||||
@ -614,12 +1117,62 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const httpMethodOptions = () => {
|
||||||
|
return [{
|
||||||
|
value: 'POST',
|
||||||
|
label: 'POST'
|
||||||
|
}, {
|
||||||
|
value: 'GET',
|
||||||
|
label: 'GET'
|
||||||
|
}, {
|
||||||
|
value: 'PUT',
|
||||||
|
label: 'PUT'
|
||||||
|
}, {
|
||||||
|
value: 'DELETE',
|
||||||
|
label: 'DELETE'
|
||||||
|
}, {
|
||||||
|
value: 'PATCH',
|
||||||
|
label: 'PATCH'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
const httpNodeData = ref({
|
||||||
|
config: [
|
||||||
|
{ name: "url", type: "input", input: "", disabled: true, required: true },
|
||||||
|
{ name: "method", type: "input", input: "POST", componentType: 'select', selectOptions: httpMethodOptions(), disabled: true },
|
||||||
|
{ name: "timeout", type: "input", input: "10000", componentType: 'number', disabled: true }
|
||||||
|
],
|
||||||
|
headers: [
|
||||||
|
{ name: "", type: "input", input: ""}
|
||||||
|
],
|
||||||
|
params: [
|
||||||
|
{ name: "", type: "input", input: ""}
|
||||||
|
],
|
||||||
|
body: {
|
||||||
|
type: 'json',
|
||||||
|
json: '{}',
|
||||||
|
formData: [
|
||||||
|
{ name: "", type: "input", input: ""}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
|
||||||
|
const addHttpNodeFormItem = (e, type) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (type === 'body') {
|
||||||
|
httpNodeData.value.body.formData.push({ name: "", type: "input", input: ""});
|
||||||
|
} else {
|
||||||
|
httpNodeData.value[type].push({ name: "", type: "input", input: ""});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
() => {
|
() => {
|
||||||
if (props.data.type === "start") {
|
if (props.data.type === "start") {
|
||||||
formData.inputParams = props.data.properties?.inputParams || [];
|
formData.inputParams = props.data.properties?.inputParams || [];
|
||||||
} else if (['currentLoop', 'serviceNode'].includes(props.data.type)) {
|
} else if (['currentLoop', 'serviceNode', 'sleep', 'loop'].includes(props.data.type)) {
|
||||||
formData.nodeParams = props.data.properties.nodeParams;
|
formData.nodeParams = props.data.properties.nodeParams;
|
||||||
formData.outputParams = props.data.properties.outputParams;
|
formData.outputParams = props.data.properties.outputParams;
|
||||||
} else if (props.data.type === "code") {
|
} else if (props.data.type === "code") {
|
||||||
@ -627,12 +1180,14 @@ watch(
|
|||||||
formData.outputParams = props.data.properties.outputParams;
|
formData.outputParams = props.data.properties.outputParams;
|
||||||
const codeParams = props.data.properties.nodeParams.find(item => item.name === 'code')
|
const codeParams = props.data.properties.nodeParams.find(item => item.name === 'code')
|
||||||
if (codeParams) {
|
if (codeParams) {
|
||||||
nextTick(() => {
|
setTimeout(() => {
|
||||||
if (editorInstance) {
|
if (editorInstance) {
|
||||||
setValue(codeParams.input)
|
setValue(codeParams.input)
|
||||||
}
|
}
|
||||||
})
|
}, 100)
|
||||||
}
|
}
|
||||||
|
} else if (props.data.type === 'http') {
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -641,7 +1196,6 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
disposeEditor()
|
disposeEditor()
|
||||||
})
|
})
|
||||||
@ -651,6 +1205,14 @@ onUnmounted(() => {
|
|||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.http-form {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.el-collapse {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form__container {
|
.form__container {
|
||||||
margin: 12px 0;
|
margin: 12px 0;
|
||||||
|
|
||||||
|
|||||||
@ -171,6 +171,7 @@ export const collapseList = [
|
|||||||
nodeParams: [
|
nodeParams: [
|
||||||
{ name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true }
|
{ name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true }
|
||||||
],
|
],
|
||||||
|
outputParams: [],
|
||||||
outputType: 'json'
|
outputType: 'json'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -196,6 +197,7 @@ export const collapseList = [
|
|||||||
nodeParams: [
|
nodeParams: [
|
||||||
{ name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true }
|
{ name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true }
|
||||||
],
|
],
|
||||||
|
outputParams: [],
|
||||||
outputType: 'json'
|
outputType: 'json'
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -213,6 +215,7 @@ export const collapseList = [
|
|||||||
nodeParams: [
|
nodeParams: [
|
||||||
{ name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true }
|
{ name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true }
|
||||||
],
|
],
|
||||||
|
outputParams: [],
|
||||||
outputType: 'json'
|
outputType: 'json'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -239,6 +242,9 @@ export const collapseList = [
|
|||||||
type: "loop",
|
type: "loop",
|
||||||
action: 'START_LOOP',
|
action: 'START_LOOP',
|
||||||
desc: "实现对列表对象循环执行一系列任务",
|
desc: "实现对列表对象循环执行一系列任务",
|
||||||
|
nodeParams: [{ name: "loopNum", type: "input", input: null, quote: "" }],
|
||||||
|
outputParams: [],
|
||||||
|
outputType: 'json'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: stopLoopSvg,
|
icon: stopLoopSvg,
|
||||||
@ -275,8 +281,9 @@ export const collapseList = [
|
|||||||
desc: "用于睡眠整个流程,表示延迟多少毫秒",
|
desc: "用于睡眠整个流程,表示延迟多少毫秒",
|
||||||
action: 'sleep',
|
action: 'sleep',
|
||||||
nodeParams: [
|
nodeParams: [
|
||||||
{ name: "delayMs", type: "input", componentType: 'number', input: "", disabled: true }
|
{ name: "delayMs", type: "input", componentType: 'number', input: 0, disabled: true }
|
||||||
],
|
],
|
||||||
|
outputParams: [],
|
||||||
outputType: 'json'
|
outputType: 'json'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -303,7 +310,18 @@ export const collapseList = [
|
|||||||
action: 'CODE',
|
action: 'CODE',
|
||||||
canAddFormItem: true,
|
canAddFormItem: true,
|
||||||
nodeParams: [
|
nodeParams: [
|
||||||
{ name: "input", type: "input", input: "", required: true }
|
{ name: "input", type: "input", input: "", required: true },
|
||||||
|
{ name: "code", type: "input", input: `
|
||||||
|
// 方法定义不能修改
|
||||||
|
function handler(params) {
|
||||||
|
// 返回值是一个可序列化成 json 的 dict 或 object
|
||||||
|
const result ={
|
||||||
|
type: 2,
|
||||||
|
message: params.input
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
`, required: true }
|
||||||
],
|
],
|
||||||
outputType: 'json',
|
outputType: 'json',
|
||||||
outputParams: [{ name: 'result', type: 'object', children: [
|
outputParams: [{ name: 'result', type: 'object', children: [
|
||||||
|
|||||||
@ -145,14 +145,14 @@ class HttpNodeHtmlModel extends HtmlNodeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 验证表单
|
// 验证表单
|
||||||
async validateForm() {
|
// async validateForm() {
|
||||||
const result = this.componentInstance.validateForm()
|
// const result = this.componentInstance.validateForm()
|
||||||
return result
|
// return result
|
||||||
}
|
// }
|
||||||
|
|
||||||
setCustomProperties() {
|
// setCustomProperties() {
|
||||||
this.componentInstance.setNodeProperties()
|
// this.componentInstance.setNodeProperties()
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置节点属性
|
* 设置节点属性
|
||||||
|
|||||||
@ -44,188 +44,16 @@
|
|||||||
@zoom="zoom"
|
@zoom="zoom"
|
||||||
@setNodeName="setNodeName"
|
@setNodeName="setNodeName"
|
||||||
/>
|
/>
|
||||||
<div class="input__container" v-show="nodeZoom">
|
|
||||||
<div class="title">
|
|
||||||
<div class="left">
|
|
||||||
<div class="tag"></div>
|
|
||||||
<div class="text">输入</div>
|
|
||||||
</div>
|
|
||||||
<div class="right" v-if="properties.properties?.canAddFormItem">
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
<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="property?.disabled || false"
|
|
||||||
v-model="property.name"
|
|
||||||
@keydown="handleInputKeydown"
|
|
||||||
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: property?.required ?? true,
|
|
||||||
message: '请输入参数值',
|
|
||||||
trigger: 'blur',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
:prop="`nodeParams.${index}.input`"
|
|
||||||
>
|
|
||||||
<el-input-number
|
|
||||||
v-if="property.componentType === 'number'"
|
|
||||||
v-model="property.input"
|
|
||||||
:min="0"
|
|
||||||
:controls="false"
|
|
||||||
:step-strictly="true"
|
|
||||||
placeholder="请输入"
|
|
||||||
clearable
|
|
||||||
@keydown="handleInputKeydown"
|
|
||||||
/>
|
|
||||||
<el-select
|
|
||||||
v-model="property.input"
|
|
||||||
v-else-if="property.componentType === 'select'"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in property.selectOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
<el-input
|
|
||||||
@keydown="handleInputKeydown"
|
|
||||||
v-else
|
|
||||||
v-model="property.input"
|
|
||||||
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 class="output__container" v-show="nodeZoom">
|
|
||||||
<div v-if="props.properties?.outputParams?.length > 0">
|
|
||||||
<div class="title">
|
|
||||||
<div class="left">
|
|
||||||
<div class="tag"></div>
|
|
||||||
<div class="text">输出</div>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<el-button
|
|
||||||
:disabled="flowStore.disableForm"
|
|
||||||
@click="addOutputFormItem"
|
|
||||||
class="addFormItem"
|
|
||||||
>
|
|
||||||
<el-icon :size="20"><Plus /></el-icon>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form__container">
|
|
||||||
<el-form
|
|
||||||
:inline="true"
|
|
||||||
:model="formData"
|
|
||||||
label-position="top"
|
|
||||||
label-width="auto"
|
|
||||||
:rules="outputRules"
|
|
||||||
ref="outputFormRef"
|
|
||||||
>
|
|
||||||
<FormItemRecursive
|
|
||||||
formType="output"
|
|
||||||
:current-list="formData.outputParams"
|
|
||||||
prop-path="outputParams"
|
|
||||||
:depth="0"
|
|
||||||
:is-first-level="true"
|
|
||||||
:endDepth="2"
|
|
||||||
:parent-path="[]"
|
|
||||||
@delete-item="deleteTopLevelItem"
|
|
||||||
/>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted, onUnmounted, nextTick } from "vue";
|
import { ref, reactive, onMounted, onUnmounted } from "vue";
|
||||||
import { getInput, initNodeZoom } from "@/utils/flow";
|
|
||||||
import { useFlowStore } from "@/store/modules/flow";
|
|
||||||
import { Plus } from "@element-plus/icons-vue";
|
|
||||||
import NodeTitle from "../../components/NodeTitle.vue";
|
import NodeTitle from "../../components/NodeTitle.vue";
|
||||||
import NodeState from "../../components/NodeState.vue";
|
import NodeState from "../../components/NodeState.vue";
|
||||||
import "vue3-json-viewer/dist/index.css";
|
import "vue3-json-viewer/dist/index.css";
|
||||||
import { emitter } from "@/utils/eventBus";
|
import { emitter } from "@/utils/eventBus";
|
||||||
import IPlayer from "@/views/device/register/components/LogicFlow/IPlayer/index.vue";
|
import IPlayer from "@/views/device/register/components/LogicFlow/IPlayer/index.vue";
|
||||||
import FormItemRecursive from "../common/FormItemRecursive.vue";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
model: Object,
|
model: Object,
|
||||||
@ -234,70 +62,6 @@ const props = defineProps({
|
|||||||
|
|
||||||
const emits = defineEmits(["contentChange"]);
|
const emits = defineEmits(["contentChange"]);
|
||||||
|
|
||||||
const flowStore = useFlowStore();
|
|
||||||
|
|
||||||
const formData = reactive({
|
|
||||||
nodeParams: [],
|
|
||||||
outputParams: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const rules = reactive({});
|
|
||||||
|
|
||||||
const quoteOptions = ref([]);
|
|
||||||
const handleTypeChange = (index) => {
|
|
||||||
if (formData.nodeParams[index].type === "input") {
|
|
||||||
formData.nodeParams[index].quote = "";
|
|
||||||
} else {
|
|
||||||
formData.nodeParams[index].input = "";
|
|
||||||
const option = getInput(props.model.id);
|
|
||||||
if (option) {
|
|
||||||
quoteOptions.value = option;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const cascaderRefs = ref([]);
|
|
||||||
const outputRules = reactive({});
|
|
||||||
const cascaderChange = (value, index) => {
|
|
||||||
const selectedOptions = cascaderRefs.value[index].getCheckedNodes(true);
|
|
||||||
formData.nodeParams[index].quote = value;
|
|
||||||
formData.nodeParams[index].quoteType = selectedOptions[0].data.type;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addOutputFormItem = () => {
|
|
||||||
formData.outputParams.push({
|
|
||||||
name: "",
|
|
||||||
type: "",
|
|
||||||
desc: "",
|
|
||||||
children: [],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const dynamicForm = ref();
|
|
||||||
const outputFormRef = ref();
|
|
||||||
const setNodeProperties = async () => {
|
|
||||||
try {
|
|
||||||
const result = await dynamicForm.value.validate();
|
|
||||||
let flag = true;
|
|
||||||
if (outputFormRef.value) {
|
|
||||||
flag = await outputFormRef.value.validate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result && flag) {
|
|
||||||
const data = toRaw(formData);
|
|
||||||
const properties = lf.getProperties(props.model.id);
|
|
||||||
lf.setProperties(props.model.id, {
|
|
||||||
...properties,
|
|
||||||
...data,
|
|
||||||
zoom: nodeZoom.value
|
|
||||||
});
|
|
||||||
emits("contentChange");
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// dynamicForm.value.clearValidate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setNodeName = (name) => {
|
const setNodeName = (name) => {
|
||||||
const properties = lf.getProperties(props.model.id);
|
const properties = lf.getProperties(props.model.id);
|
||||||
lf.setProperties(props.model.id, {
|
lf.setProperties(props.model.id, {
|
||||||
@ -312,115 +76,6 @@ const inputJsonData = ref({});
|
|||||||
const outputJsonData = ref({});
|
const outputJsonData = ref({});
|
||||||
const errorInfoData = ref('');
|
const errorInfoData = ref('');
|
||||||
|
|
||||||
const visibleChange = (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(() => {
|
|
||||||
formData.nodeParams[index].quote = currentValue;
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
// 手动触发级联选择器的重新渲染
|
|
||||||
// cascaderRefs.value[index].updatePopper();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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: "验证失败" };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const addFormItem = () => {
|
|
||||||
formData.nodeParams.push({
|
|
||||||
name: "",
|
|
||||||
type: "",
|
|
||||||
desc: "",
|
|
||||||
required: false,
|
|
||||||
children: [],
|
|
||||||
});
|
|
||||||
emits("contentChange");
|
|
||||||
};
|
|
||||||
|
|
||||||
// 删除顶层表单项
|
|
||||||
const deleteTopLevelItem = (fullPath) => {
|
|
||||||
// 从顶层数据开始查找
|
|
||||||
let currentLevel = formData.outputParams;
|
|
||||||
|
|
||||||
// 遍历路径(除最后一个索引,因为最后一个是要删除的项)
|
|
||||||
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");
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodeZoom = ref(props?.properties?.zoom ?? true)
|
|
||||||
const zoom = (flag) => {
|
|
||||||
nodeZoom.value = flag
|
|
||||||
initNodeZoom(props.model.id, nodeZoom.value, '.node__box')
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
initNodeZoom(props.model.id, props?.properties?.zoom ?? true, '.node__box', true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
props.properties.outputParams &&
|
|
||||||
props.properties.outputParams.length > 0
|
|
||||||
) {
|
|
||||||
formData.outputParams = props.properties.outputParams;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
deep: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleInputKeydown = (e) => {
|
|
||||||
// 阻止事件冒泡到 Logic Flow 节点,避免被其事件拦截
|
|
||||||
e.stopPropagation();
|
|
||||||
// 可选:明确放行 Ctrl+V(增强兼容性)
|
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
||||||
e.returnValue = true; // 允许默认粘贴行为
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
emitter.on("changeNodeState", (data) => {
|
emitter.on("changeNodeState", (data) => {
|
||||||
@ -450,16 +105,18 @@ onMounted(() => {
|
|||||||
emits("contentChange");
|
emits("contentChange");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
emitter.on("setProperties", (data) => {
|
||||||
|
if (data.id === props.model.id) {
|
||||||
|
emits("contentChange");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
emitter.off("changeNodeState");
|
emitter.off("changeNodeState");
|
||||||
emitter.off("contentChange");
|
emitter.off("contentChange");
|
||||||
});
|
emitter.off("setProperties");
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
validateForm,
|
|
||||||
setNodeProperties,
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -467,151 +124,5 @@ defineExpose({
|
|||||||
.node__container {
|
.node__container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.output__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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-row) {
|
|
||||||
align-items: end;
|
|
||||||
|
|
||||||
.el-input {
|
|
||||||
--el-input-width: 148px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-select {
|
|
||||||
--el-select-width: 148px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-cascader {
|
|
||||||
--el-form-inline-content-width: 148px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -21,21 +21,21 @@ class loopBodyModel extends GroupNodeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 验证表单
|
// 验证表单
|
||||||
async validateForm() {
|
// async validateForm() {
|
||||||
if (this.componentInstance && this.componentInstance.validate) {
|
// if (this.componentInstance && this.componentInstance.validate) {
|
||||||
try {
|
// try {
|
||||||
const result = await this.componentInstance.validate();
|
// const result = await this.componentInstance.validate();
|
||||||
if (result) {
|
// if (result) {
|
||||||
return { valid: true, message: "验证通过" };
|
// return { valid: true, message: "验证通过" };
|
||||||
} else {
|
// } else {
|
||||||
return { valid: false, message: "验证失败" };
|
// return { valid: false, message: "验证失败" };
|
||||||
}
|
// }
|
||||||
} catch {
|
// } catch {
|
||||||
return { valid: false, message: "验证失败" };
|
// return { valid: false, message: "验证失败" };
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return { valid: true, message: "无验证方法" };
|
// return { valid: true, message: "无验证方法" };
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 计算子节点的包围盒(包含所有子节点的最小矩形区域)
|
// 计算子节点的包围盒(包含所有子节点的最小矩形区域)
|
||||||
getChildrenBBox() {
|
getChildrenBBox() {
|
||||||
@ -59,8 +59,8 @@ class loopBodyModel extends GroupNodeModel {
|
|||||||
minY = Math.min(minY, node.y - height / 2);
|
minY = Math.min(minY, node.y - height / 2);
|
||||||
maxY = Math.max(maxY, node.y + height / 2);
|
maxY = Math.max(maxY, node.y + height / 2);
|
||||||
});
|
});
|
||||||
this.width = maxX - minX + 300; // 增加内边距
|
this.width = maxX - minX + 50; // 增加内边距
|
||||||
this.height = maxY - minY + 260;
|
this.height = maxY - minY + 50;
|
||||||
this.x = (minX + maxX) / 2;
|
this.x = (minX + maxX) / 2;
|
||||||
this.y = (minY + maxY) / 2 - 120;
|
this.y = (minY + maxY) / 2 - 120;
|
||||||
const properties = lf.getProperties(this.id)
|
const properties = lf.getProperties(this.id)
|
||||||
@ -101,14 +101,14 @@ class loopBodyModel extends GroupNodeModel {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
x: x + width / 2 ,
|
x: x + width / 2 ,
|
||||||
y: y - 25,
|
y: y + height / 2,
|
||||||
name: "right",
|
name: "right",
|
||||||
id: "".concat(this.id, "_1"),
|
id: "".concat(this.id, "_1"),
|
||||||
properties: { connectionType: "source" },
|
properties: { connectionType: "source" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x: x - width / 2,
|
x: x - width / 2,
|
||||||
y: y - 25,
|
y: y + height / 2,
|
||||||
name: "left",
|
name: "left",
|
||||||
id: "".concat(this.id, "_3"),
|
id: "".concat(this.id, "_3"),
|
||||||
properties: { connectionType: "target" },
|
properties: { connectionType: "target" },
|
||||||
|
|||||||
@ -9,122 +9,21 @@
|
|||||||
:showZoom="false"
|
:showZoom="false"
|
||||||
@setNodeName="setNodeName"
|
@setNodeName="setNodeName"
|
||||||
/>
|
/>
|
||||||
<div class="loop__container">
|
<div>循环体</div>
|
||||||
<div>
|
<div
|
||||||
<el-form
|
class="child__container"
|
||||||
:inline="true"
|
@drop="handleDrop"
|
||||||
:model="formData"
|
@dragover="handleDragover"
|
||||||
:rules="rules"
|
>
|
||||||
ref="dynamicForm"
|
<slot></slot>
|
||||||
label-position="top"
|
</div>
|
||||||
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"
|
|
||||||
class="param-name"
|
|
||||||
v-model="property.name"
|
|
||||||
placeholder="请输入"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
:label="index === 0 ? '参数值' : ''"
|
|
||||||
:prop="`nodeParams.${index}.type`"
|
|
||||||
>
|
|
||||||
<el-select
|
|
||||||
class="param-type"
|
|
||||||
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
|
|
||||||
class="param-value"
|
|
||||||
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
|
|
||||||
class="param-value"
|
|
||||||
: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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import NodeTitle from "../../components/NodeTitle.vue";
|
import NodeTitle from "../../components/NodeTitle.vue";
|
||||||
import loop from "../../icon/loop.svg";
|
import loop from "../../icon/loop.svg";
|
||||||
import { useFlowStore } from "@/store/modules/flow";
|
import { addNewEdge } from "@/utils/flow";
|
||||||
import { addNewEdge, getInput } from "@/utils/flow";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
model: Object,
|
model: Object,
|
||||||
@ -133,25 +32,6 @@ const props = defineProps({
|
|||||||
nowTime: Number,
|
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 emits = defineEmits(["bindRef", "addToGroup", "contentChange"]);
|
||||||
|
|
||||||
@ -183,26 +63,6 @@ const handleDrop = (e) => {
|
|||||||
}, 50);
|
}, 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 setNodeName = (name) => {
|
||||||
const properties = lf.getProperties(props.model.id);
|
const properties = lf.getProperties(props.model.id);
|
||||||
lf.setProperties(props.model.id, {
|
lf.setProperties(props.model.id, {
|
||||||
@ -212,60 +72,6 @@ const setNodeName = (name) => {
|
|||||||
emits("contentChange");
|
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>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.group__container {
|
.group__container {
|
||||||
@ -286,30 +92,6 @@ onMounted(() => {
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.loop__container) {
|
|
||||||
height: auto;
|
|
||||||
|
|
||||||
.el-row {
|
|
||||||
align-items: end;
|
|
||||||
|
|
||||||
.el-form-item {
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-name {
|
|
||||||
width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-type {
|
|
||||||
width: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-desc {
|
|
||||||
width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.child__container {
|
.child__container {
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@ -29,116 +29,6 @@
|
|||||||
@zoom="zoom"
|
@zoom="zoom"
|
||||||
@setNodeName="setNodeName"
|
@setNodeName="setNodeName"
|
||||||
/>
|
/>
|
||||||
<div class="input__container" v-show="nodeZoom">
|
|
||||||
<div class="title">
|
|
||||||
<div class="left">
|
|
||||||
<div class="tag"></div>
|
|
||||||
<div class="text">输入</div>
|
|
||||||
</div>
|
|
||||||
<div class="right" v-if="properties.properties?.canAddFormItem">
|
|
||||||
<el-button
|
|
||||||
:disabled="flowStore.disableForm"
|
|
||||||
@click="addFormItem"
|
|
||||||
class="addFormItem"
|
|
||||||
>
|
|
||||||
<el-icon :size="20"><Plus /></el-icon>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form__container" v-show="nodeZoom">
|
|
||||||
<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="property?.disabled || false"
|
|
||||||
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-if="property.componentType === 'number'" v-model="property.input" :min="0" :controls="false" :step-strictly="true" placeholder="请输入" clearable />
|
|
||||||
<el-select v-model="property.input" v-else-if="property.componentType === 'select'" >
|
|
||||||
<el-option v-for="item in property.selectOptions" :key="item" :label="item" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
<el-input v-else v-model="property.input" 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"
|
|
||||||
:options="quoteOptions"
|
|
||||||
placeholder="请选择"
|
|
||||||
@visible-change="visibleChange"
|
|
||||||
@change="(value) => cascaderChange(value, index)"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form-item>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="output__container" v-if="props.properties?.outputParams?.length > 0">
|
|
||||||
<div class="title">
|
|
||||||
<div class="tag"></div>
|
|
||||||
<div class="text">输出</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form__container">
|
|
||||||
<el-form
|
|
||||||
:inline="true"
|
|
||||||
label-position="top"
|
|
||||||
label-width="auto"
|
|
||||||
:disabled="true"
|
|
||||||
>
|
|
||||||
<el-row v-for="(item, index) in props.properties.outputParams">
|
|
||||||
<el-form-item label="参数名" >
|
|
||||||
<el-input :value="item.name" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="参数类型" >
|
|
||||||
<el-input :value="item.type" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="描述" >
|
|
||||||
<el-input :value="item.desc" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -146,7 +36,6 @@
|
|||||||
import { ref, reactive, onMounted, onUnmounted, nextTick } from "vue";
|
import { ref, reactive, onMounted, onUnmounted, nextTick } from "vue";
|
||||||
import { getInput, initNodeZoom } from "@/utils/flow";
|
import { getInput, initNodeZoom } from "@/utils/flow";
|
||||||
import { useFlowStore } from "@/store/modules/flow";
|
import { useFlowStore } from "@/store/modules/flow";
|
||||||
import { Plus } from "@element-plus/icons-vue";
|
|
||||||
import NodeTitle from "../../components/NodeTitle.vue";
|
import NodeTitle from "../../components/NodeTitle.vue";
|
||||||
import NodeState from "../../components/NodeState.vue";
|
import NodeState from "../../components/NodeState.vue";
|
||||||
import "vue3-json-viewer/dist/index.css";
|
import "vue3-json-viewer/dist/index.css";
|
||||||
@ -160,46 +49,10 @@ const props = defineProps({
|
|||||||
|
|
||||||
const emits = defineEmits(["contentChange"]);
|
const emits = defineEmits(["contentChange"]);
|
||||||
|
|
||||||
const flowStore = useFlowStore();
|
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
nodeParams: [],
|
nodeParams: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const rules = reactive({});
|
|
||||||
|
|
||||||
const quoteOptions = ref([]);
|
|
||||||
const handleTypeChange = (index) => {
|
|
||||||
if (formData.nodeParams[index].type === "input") {
|
|
||||||
formData.nodeParams[index].quote = "";
|
|
||||||
} else {
|
|
||||||
formData.nodeParams[index].input = "";
|
|
||||||
const option = getInput(props.model.id);
|
|
||||||
if (option) {
|
|
||||||
quoteOptions.value = option;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const dynamicForm = ref();
|
|
||||||
|
|
||||||
const setNodeProperties = async () => {
|
|
||||||
try {
|
|
||||||
const result = await dynamicForm.value.validate();
|
|
||||||
if (result) {
|
|
||||||
const data = toRaw(formData);
|
|
||||||
const properties = lf.getProperties(props.model.id);
|
|
||||||
lf.setProperties(props.model.id, {
|
|
||||||
...properties,
|
|
||||||
...data,
|
|
||||||
zoom: nodeZoom.value
|
|
||||||
});
|
|
||||||
emits("contentChange");
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
dynamicForm.value.clearValidate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setNodeName = (name) => {
|
const setNodeName = (name) => {
|
||||||
const properties = lf.getProperties(props.model.id);
|
const properties = lf.getProperties(props.model.id);
|
||||||
@ -214,72 +67,6 @@ const nodeOperatingStatus = ref("NORMAL");
|
|||||||
const inputJsonData = ref({});
|
const inputJsonData = ref({});
|
||||||
const outputJsonData = ref({});
|
const outputJsonData = ref({});
|
||||||
|
|
||||||
const visibleChange = (value) => {
|
|
||||||
if (value) {
|
|
||||||
const option = getInput(props.model.id);
|
|
||||||
if (option) {
|
|
||||||
quoteOptions.value = option;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
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: "验证失败" };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const addFormItem = () => {
|
|
||||||
formData.nodeParams.push({
|
|
||||||
name: "",
|
|
||||||
type: "",
|
|
||||||
desc: "",
|
|
||||||
required: false,
|
|
||||||
children: [],
|
|
||||||
});
|
|
||||||
emits("contentChange");
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodeZoom = ref(props?.properties?.zoom ?? true)
|
|
||||||
const zoom = (flag) => {
|
|
||||||
nodeZoom.value = flag
|
|
||||||
initNodeZoom(props.model.id, nodeZoom.value, '.node__box')
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
nextTick(() => {
|
|
||||||
initNodeZoom(props.model.id, props?.properties?.zoom ?? true, '.node__box', true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
deep: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
emitter.on("changeNodeState", (data) => {
|
emitter.on("changeNodeState", (data) => {
|
||||||
if (data.nodeId === props.model.id) {
|
if (data.nodeId === props.model.id) {
|
||||||
@ -307,154 +94,25 @@ onMounted(() => {
|
|||||||
emits("contentChange");
|
emits("contentChange");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
emitter.on("setProperties", (data) => {
|
||||||
|
if (data.id === props.model.id) {
|
||||||
|
emits("contentChange");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
emitter.off("changeNodeState");
|
emitter.off("changeNodeState");
|
||||||
emitter.off("contentChange");
|
emitter.off("contentChange");
|
||||||
|
emitter.off("setProperties");
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
validateForm,
|
|
||||||
setNodeProperties,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.node__container {
|
.node__container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.output__container {
|
|
||||||
width: 100%;
|
|
||||||
background-color: #fafbfc;
|
|
||||||
padding: 0 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 32px;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form__container {
|
|
||||||
margin: 12px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-row) {
|
|
||||||
align-items: end;
|
|
||||||
|
|
||||||
.el-input {
|
|
||||||
--el-input-width: 170px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-select {
|
|
||||||
--el-select-width: 170px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-cascader {
|
|
||||||
--el-form-inline-content-width: 170px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user