Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions apps/application/flow/step_node/form_node/impl/base_form_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
from application.flow.common import Answer
from application.flow.i_step_node import NodeResult
from application.flow.step_node.form_node.i_form_node import IFormNode
import re

_TEMPLATE_RE = re.compile(r'\{\{([^.\s}]+)\.([^.\s}]+)\}\}')
multi_select_list = [
'MultiSelect',
'MultiRow'
Expand Down Expand Up @@ -106,8 +108,63 @@ def reset_field(self, field):
field['default_value'] = self.workflow_manage.get_reference_field(field.get('default_value')[0],
field.get('default_value')[1:])

visibility_rules = field.get('visibility_rules')
if visibility_rules and isinstance(visibility_rules.get('conditions'), list):
for cond in visibility_rules['conditions']:
cond_field = cond.get('field')
if not cond_field or len(cond_field) < 2 or not cond_field[0] or not cond_field[1]:
continue

# cross node -------> _left
if cond_field[0] != self.node.id:
cond['_left'] = self.workflow_manage.get_reference_field(cond_field[0], cond_field[1:])
# 右值 {{}}
cond_value = cond.get("value")
if isinstance(cond_value, str) and _TEMPLATE_RE.search(cond_value):
cond['value'] = self._render_cond_value(cond_value)

return field

def _render_cond_value(self, value):
"""
render cross-node/global/chat {{}} to literal, preserve same-form {{}}
match.group(0) → "{{开始.question}}" # 完整匹配
match.group(1) → "开始" # 第一个 () 捕获的
match.group(2) → "question" # 第二个 () 捕获的
match.start() → 3 # 匹配起始位置
match.end() → 16 # 匹配结束位置
"""
def replacer(match):
node_display = match.group(1)
field_name = match.group(2)

# field_list: cross_node
for f in self.workflow_manage.field_list:
if f.get('node_name') == node_display and f.get('value') == field_name:
if f.get('node_id') == self.node.id:
return match.group(0) # same node
ref = self.workflow_manage.get_reference_field(f.get('node_id'),[field_name])
return str(ref) if ref is not None else ''

# global
if node_display in ('全局变量', 'global'):
for f in self.workflow_manage.global_field_list:
if f.get('value') == field_name:
ref = self.workflow_manage.get_reference_field('global', [field_name])
return str(ref) if ref is not None else ''

# chat
if node_display == 'chat':
for f in self.workflow_manage.chat_field_list:
if f.get("value") == field_name:
ref = self.workflow_manage.get_reference_field('chat', [field_name])
return str(ref) if ref is not None else ''
return match.group(0)
try:
return _TEMPLATE_RE.sub(replacer, value)
except Exception:
return value

def execute(self, form_field_list, form_content_format, form_data, **kwargs) -> NodeResult:
if form_data is not None:
self.context['is_submit'] = True
Expand Down
184 changes: 107 additions & 77 deletions ui/src/components/dynamics-form/constructor/index.vue
Original file line number Diff line number Diff line change
@@ -1,91 +1,111 @@
<template>
<el-form
@submit.prevent
ref="ruleFormRef"
class="mb-24"
label-width="auto"
:model="form_data"
v-bind="$attrs"
>
<el-form-item
:label="$t('dynamicsForm.paramForm.field.label')"
:required="true"
prop="field"
:rules="rules.field"
>
<el-input
v-model="form_data.field"
:maxlength="64"
:placeholder="$t('dynamicsForm.paramForm.field.placeholder')"
show-word-limit
/>
</el-form-item>
<el-form-item
:label="$t('dynamicsForm.paramForm.name.label')"
:required="true"
prop="label"
:rules="rules.label"
>
<el-input
v-model="form_data.label"
:maxlength="64"
show-word-limit
:placeholder="$t('dynamicsForm.paramForm.name.placeholder')"
/>
</el-form-item>
<el-form-item :label="$t('dynamicsForm.paramForm.tooltip.label')">
<el-input
v-model="form_data.tooltip"
:maxlength="128"
show-word-limit
:placeholder="$t('dynamicsForm.paramForm.tooltip.placeholder')"
/>
</el-form-item>
<el-form-item
:label="$t('dynamicsForm.paramForm.required.label')"
:required="true"
prop="required"
:rules="rules.required"
@click.prevent
>
<el-switch v-model="form_data.required" :active-value="true" :inactive-value="false" />
</el-form-item>
<el-form-item
:label="$t('dynamicsForm.paramForm.input_type.label')"
:required="true"
prop="input_type"
:rules="rules.input_type"
>
<el-select
v-model="form_data.input_type"
:placeholder="$t('dynamicsForm.paramForm.input_type.placeholder')"
<el-tabs v-model="activeTab">
<el-tab-pane :label="$t('dynamicsForm.paramForm.basicInfo', '基本信息')" name="basic">
<el-form
@submit.prevent
ref="ruleFormRef"
class="mb-24"
label-width="auto"
:model="form_data"
v-bind="$attrs"
>
<el-option
v-for="input_type in input_type_list"
:key="input_type.value"
:label="input_type.label"
:value="input_type.value"
/>
</el-select>
</el-form-item>
<component
v-if="form_data.input_type"
ref="componentFormRef"
v-model="form_data"
:is="form_data.input_type"
></component>
</el-form>
<el-form-item
:label="$t('dynamicsForm.paramForm.field.label')"
:required="true"
prop="field"
:rules="rules.field"
>
<el-input
v-model="form_data.field"
:maxlength="64"
:placeholder="$t('dynamicsForm.paramForm.field.placeholder')"
show-word-limit
/>
</el-form-item>
<el-form-item
:label="$t('dynamicsForm.paramForm.name.label')"
:required="true"
prop="label"
:rules="rules.label"
>
<el-input
v-model="form_data.label"
:maxlength="64"
show-word-limit
:placeholder="$t('dynamicsForm.paramForm.name.placeholder')"
/>
</el-form-item>
<el-form-item :label="$t('dynamicsForm.paramForm.tooltip.label')">
<el-input
v-model="form_data.tooltip"
:maxlength="128"
show-word-limit
:placeholder="$t('dynamicsForm.paramForm.tooltip.placeholder')"
/>
</el-form-item>
<el-form-item
:label="$t('dynamicsForm.paramForm.required.label')"
:required="true"
prop="required"
:rules="rules.required"
@click.prevent
>
<el-switch v-model="form_data.required" :active-value="true" :inactive-value="false" />
</el-form-item>
<el-form-item
:label="$t('dynamicsForm.paramForm.input_type.label')"
:required="true"
prop="input_type"
:rules="rules.input_type"
>
<el-select
v-model="form_data.input_type"
:placeholder="$t('dynamicsForm.paramForm.input_type.placeholder')"
>
<el-option
v-for="input_type in input_type_list"
:key="input_type.value"
:label="input_type.label"
:value="input_type.value"
/>
</el-select>
</el-form-item>
<component
v-if="form_data.input_type"
ref="componentFormRef"
v-model="form_data"
:is="form_data.input_type"
></component>
</el-form>
</el-tab-pane>

<el-tab-pane label="显隐设置" name="visibility">
<VisibilityConstructor
ref="visibilityRef"
:initialValue="visibility_rules"
:nodeModel="nodeModel"
:currentNodeFields="currentNodeFields"
:currentEditingIndex="currentEditingIndex"
/>
</el-tab-pane>
</el-tabs>
</template>

<script setup lang="ts">
import { onMounted, ref, nextTick } from 'vue'
import type { FormInstance } from 'element-plus'
import _ from 'lodash'
import { input_type_list as input_type_list_data } from '@/components/dynamics-form/constructor/data'
import { t } from '@/locales'
import VisibilityConstructor from '../visibility/Constructor.vue'

const props = withDefaults(
defineProps<{
modelValue?: any
input_type_list?: Array<{ label: string; value: string }>
nodeModel?: any
currentNodeFields?: Array<any>
currentEditingIndex?: number
}>(),
{
input_type_list: () =>
Expand All @@ -97,6 +117,7 @@ const props = withDefaults(
)
const emit = defineEmits(['update:modelValue'])

const activeTab = ref('basic')
const ruleFormRef = ref<FormInstance>()

const componentFormRef = ref<any>()
Expand All @@ -107,6 +128,8 @@ const form_data = ref<any>({
required: false,
input_type: '',
})
const visibility_rules = ref<any>(null)
const visibilityRef = ref()
const rules = {
label: [{ required: true, message: t('dynamicsForm.paramForm.name.requiredMessage') }],
field: [{ required: true, message: t('dynamicsForm.paramForm.field.requiredMessage') }],
Expand All @@ -129,15 +152,20 @@ const getData = () => {
field: form_data.value.field,
default_value: form_data.value.default_value,
show_default_value: form_data.value.show_default_value,
visibility_rules: visibilityRef.value?.getData() ?? null,
...componentFormRef.value.getData(),
}
}

const validate = () => {
const promises = []
if (ruleFormRef.value) {
return ruleFormRef.value?.validate()
promises.push(ruleFormRef.value.validate())
}
if (visibilityRef.value?.validate) {
promises.push(visibilityRef.value.validate())
}
return Promise.resolve()
return Promise.all(promises)
}

onMounted(() => {
Expand All @@ -148,6 +176,7 @@ onMounted(() => {
const rander = (data: any) => {
form_data.value.required = data.required ? data.required : false
form_data.value.field = data.field
visibility_rules.value = data.visibility_rules ?? null
if (data.show_default_value !== undefined) {
form_data.value.show_default_value = data.show_default_value
}
Expand All @@ -163,6 +192,7 @@ const rander = (data: any) => {
}
nextTick(() => {
componentFormRef.value?.rander(data)
visibilityRef.value?.restore(data.visibility_rules)
})
}

Expand Down
16 changes: 16 additions & 0 deletions ui/src/components/dynamics-form/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import type { FormInstance } from 'element-plus'
import type Result from '@/request/Result'
import _ from 'lodash'
import { get, post, put, del } from '@/request/index'
import { evaluateVisibility } from './visibility'
const request = {
get,
post,
Expand Down Expand Up @@ -102,6 +103,16 @@ const show = (field: FormField) => {
}
}
}

// new
if (field.visibility_rules?.node_id) {
return evaluateVisibility(field.visibility_rules, {
formValue: formValue.value,
currentNodeId: field.visibility_rules.node_id,
currentNodeName: field.visibility_rules.node_name || '',
})
}

return true
}

Expand Down Expand Up @@ -280,6 +291,11 @@ const getFormDefaultValue = (fieldList: Array<any>, form_data?: any) => {
* 校验函数
*/
const validate = () => {
for (const field of formFieldList.value) {
if (!show(field)) {
formValue.value[field.field] = null
}
}
return Promise.all([
...formFieldRef.value.map((item) => item.validate()),
ruleFormRef.value ? ruleFormRef.value.validate() : Promise.resolve(),
Expand Down
Loading
Loading