feat: Group field dialog

v3.2
zhangzhanwei 2025-10-27 13:43:40 +08:00 committed by zhanweizhang7
parent 42d7aeb5bf
commit 2fc4a393ad
4 changed files with 179 additions and 81 deletions

View File

@ -17,7 +17,8 @@ class VariableListSerializer(serializers.Serializer):
class VariableGroupSerializer(serializers.Serializer): class VariableGroupSerializer(serializers.Serializer):
id = serializers.CharField(required=True, label=_("Group id")) id = serializers.CharField(required=True, label=_("Group id"))
group_name = serializers.CharField(required=True, label=_("group_name")) field = serializers.CharField(required=True, label=_("group_name"))
label = serializers.CharField(required=True)
variable_list = VariableListSerializer(many=True) variable_list = VariableListSerializer(many=True)

View File

@ -38,7 +38,7 @@ class BaseVariableAggregationNode(IVariableAggregation):
'variable_to_json': self.set_variable_to_json, 'variable_to_json': self.set_variable_to_json,
} }
result = { item.get('group_name'):strategy_map[strategy](item.get('variable_list')) for item in group_list} result = { item.get('field'):strategy_map[strategy](item.get('variable_list')) for item in group_list}
return NodeResult({'result': result,**result},{}) return NodeResult({'result': result,**result},{})

View File

@ -0,0 +1,118 @@
<template>
<el-dialog
:title="isEdit ? $t('common.param.editParam') : $t('common.param.addParam')"
v-model="dialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
:destroy-on-close="true"
:before-close="close"
append-to-body
>
<el-form
label-position="top"
ref="fieldFormRef"
:rules="rules"
:model="form"
require-asterisk-position="right"
>
<el-form-item
:label="$t('dynamicsForm.paramForm.field.label')"
:required="true"
prop="field"
:rules="rules.field"
>
<el-input
v-model="form.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.label"
:maxlength="64"
show-word-limit
:placeholder="$t('dynamicsForm.paramForm.name.placeholder')"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click.prevent="close"> {{ $t('common.cancel') }} </el-button>
<el-button type="primary" @click="submit(fieldFormRef)" :loading="loading">
{{ isEdit ? $t('common.save') : $t('common.add') }}
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import type { FormInstance } from 'element-plus'
import { cloneDeep } from 'lodash'
import { t } from '@/locales'
const emit = defineEmits(['refresh'])
const fieldFormRef = ref()
const loading = ref<boolean>(false)
const isEdit = ref(false)
const currentIndex = ref(null)
const form = ref<any>({
field: '',
label: '',
})
const rules = reactive({
label: [
{ required: true, message: t('dynamicsForm.paramForm.name.requiredMessage'), trigger: 'blur' },
],
field: [
{ required: true, message: t('dynamicsForm.paramForm.field.requiredMessage'), trigger: 'blur' },
{
pattern: /^[a-zA-Z0-9_]+$/,
message: t('dynamicsForm.paramForm.field.requiredMessage2'),
trigger: 'blur',
},
],
})
const dialogVisible = ref<boolean>(false)
const open = (data: any, index?: any) => {
if (data) {
form.value = cloneDeep(data)
isEdit.value = true
currentIndex.value = index
}
dialogVisible.value = true
}
const close = () => {
dialogVisible.value = false
isEdit.value = false
currentIndex.value = null
form.value = {
field: '',
label: '',
}
}
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {
if (valid) {
emit('refresh', form.value, currentIndex.value)
}
})
}
defineExpose({ open, close })
</script>
<style lang="scss" scoped></style>

View File

@ -39,11 +39,11 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<div v-for="(group_list, gIndex) in form_data.group_list" :key="group_list.id" class="mb-8"> <div v-for="(group, gIndex) in form_data.group_list" :key="group.id" class="mb-8">
<el-card shadow="never" class="card-never" style="--el-card-padding: 12px"> <el-card shadow="never" class="card-never" style="--el-card-padding: 12px">
<div class="flex-between mb-12"> <div class="flex-between mb-12">
<el-form-item <!-- <el-form-item
v-if="editingGroupIndex === gIndex" v-if="editingGroupIndex === gIndex"
:prop="`group_list.${gIndex}.group_name`" :prop="`group_list.${gIndex}.group_name`"
:rules="groupNameRules(gIndex)" :rules="groupNameRules(gIndex)"
@ -58,10 +58,10 @@
style="width: 200px; font-weight: bold;" style="width: 200px; font-weight: bold;"
> >
</el-input> </el-input>
</el-form-item> </el-form-item> -->
<span v-else class="font-bold">{{ group_list.group_name }}</span> <span class="font-bold">{{ group.field }}</span>
<div class="flex align-center"> <div class="flex align-center">
<el-button @click="editGroupName(gIndex)" size="large" link> <el-button @click="openAddOrEditDialog(group,gIndex)" size="large" link>
<el-icon><EditPen /></el-icon> <el-icon><EditPen /></el-icon>
</el-button> </el-button>
<el-button @click="deleteGroup(gIndex)" size="large" link :disabled="form_data.group_list.length <= 1"> <el-button @click="deleteGroup(gIndex)" size="large" link :disabled="form_data.group_list.length <= 1">
@ -70,7 +70,7 @@
</div> </div>
</div> </div>
<div v-for="(item, vIndex) in group_list.variable_list" :key="item.v_id" class="mb-4"> <div v-for="(item, vIndex) in group.variable_list" :key="item.v_id" class="mb-4">
<el-row :gutter="8"> <el-row :gutter="8">
<el-col :span="21"> <el-col :span="21">
<el-form-item <el-form-item
@ -96,7 +96,7 @@
link link
size="large" size="large"
class="mt-4" class="mt-4"
:disabled="group_list.variable_list.length <= 1" :disabled="group.variable_list.length <= 1"
@click="deleteVariable(gIndex, vIndex)" @click="deleteVariable(gIndex, vIndex)"
> >
<AppIcon iconName="app-delete"></AppIcon> <AppIcon iconName="app-delete"></AppIcon>
@ -112,36 +112,37 @@
</el-card> </el-card>
</div> </div>
<el-button @click="addGroup" type="primary" size="large" link> <el-button @click="openAddOrEditDialog()" type="primary" size="large" link>
<AppIcon iconName="app-add-outlined" class="mr-4"/> <AppIcon iconName="app-add-outlined" class="mr-4"/>
{{ $t('views.applicationWorkflow.nodes.variableAggregationNode.add') }} {{ $t('views.applicationWorkflow.nodes.variableAggregationNode.add') }}
</el-button> </el-button>
</el-form> </el-form>
<GroupFieldDialog ref="GroupFieldDialogRef" @refresh="refreshFieldList"></GroupFieldDialog>
</NodeContainer> </NodeContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { set, cloneDeep, debounce } from 'lodash' import { set, cloneDeep } from 'lodash'
import NodeCascader from '@/workflow/common/NodeCascader.vue' import NodeCascader from '@/workflow/common/NodeCascader.vue'
import NodeContainer from '@/workflow/common/NodeContainer.vue' import NodeContainer from '@/workflow/common/NodeContainer.vue'
import GroupFieldDialog from './component/GroupFieldDialog.vue'
import { ref, computed, onMounted, nextTick } from 'vue' import { ref, computed, onMounted } from 'vue'
import { isLastNode } from '@/workflow/common/data' import { isLastNode } from '@/workflow/common/data'
import { t } from '@/locales' import { t } from '@/locales'
import { randomId } from '@/utils/common' import { randomId } from '@/utils/common'
import { MsgError } from '@/utils/message'
const props = defineProps<{ nodeModel: any }>() const props = defineProps<{ nodeModel: any }>()
const VariableAggregationRef = ref() const VariableAggregationRef = ref()
const nodeCascaderRef = ref() const nodeCascaderRef = ref()
const editingGroupIndex = ref<number | null>(null) const GroupFieldDialogRef = ref()
const groupNameInputRef = ref()
const form = { const form = {
strategy: 'first_non_null', strategy: 'first_non_null',
group_list: [ group_list: [
{ {
id: randomId(), id: randomId(),
group_name: 'Group1', label: 'Group1',
field: 'Group1',
variable_list: [ variable_list: [
{ {
v_id: randomId(), v_id: randomId(),
@ -165,68 +166,53 @@ const form_data = computed({
} }
}) })
const isGroupNameValid = ref<boolean>(true) const inputFieldList = ref<any[]>([])
const groupNameErrMsg = ref('')
const tempGroupName = ref('')
const editGroupName = async (gIndex: number) => { function openAddOrEditDialog(group?: any, index?: any) {
editingGroupIndex.value = gIndex let data = null
tempGroupName.value = form_data.value.group_list[gIndex].group_name if (group && index !== undefined) {
isGroupNameValid.value = true data = {
groupNameErrMsg.value = '' field: group.field,
await nextTick() label: group.label,
if (groupNameInputRef.value) { }
groupNameInputRef.value.focus()
} }
GroupFieldDialogRef.value.open(data,index)
} }
const groupNameRules = (gIndex: number) => [ function refreshFieldList(data: any, index: any) {
{ for (let i = 0; i < inputFieldList.value.length; i++) {
required: true, if (inputFieldList.value[i].field === data.field && index !== i) {
message: t('views.applicationWorkflow.nodes.variableAggregationNode.group.noneError'), MsgError(t('views.applicationWorkflow.tip.paramErrorMessage') + data.field)
trigger: 'blur' return
}, }
{
validator: (rule: any, value: string, callback: any) => {
const trimmedValue = value?.trim() || ''
const hasDuplicate = form_data.value.group_list.some((item: any, index: number) =>
index !== gIndex && item.group_name.trim() === trimmedValue
)
if (hasDuplicate) {
callback(new Error(t('views.applicationWorkflow.nodes.variableAggregationNode.group.dupError')))
} else {
callback()
}
},
trigger: 'change' //
} }
] if ([undefined, null].includes(index)) {
inputFieldList.value.push(data)
addGroup(data)
const validateGroupNameField = debounce((gIndex: number) => { } else {
VariableAggregationRef.value?.validateField(`group_list.${gIndex}.group_name`) inputFieldList.value.splice(index, 1, data)
}, 500) editGroupDesc(data, index)
const finishEditGroupName = async (gIndex: number) => {
try {
await VariableAggregationRef.value?.validateField(`group_list.${gIndex}.group_name`)
const c_group_list = cloneDeep(form_data.value.group_list)
const fields = c_group_list.map((item:any) => ({ label: item.group_name, value: item.group_name}))
set(props.nodeModel.properties.config, 'fields', fields)
editingGroupIndex.value = null
} catch (error) {
form_data.value.group_list[gIndex].group_name = tempGroupName.value
editingGroupIndex.value = null
} }
GroupFieldDialogRef.value.close()
const fields = [
...inputFieldList.value.map((item) => ({ label: item.label, value: item.field })),
]
set(props.nodeModel.properties.config, 'fields', fields)
}
const editGroupDesc = (data: any, gIndex: any) => {
const c_group_list = cloneDeep(form_data.value.group_list)
c_group_list[gIndex].field = data.field
c_group_list[gIndex].label = data.label
form_data.value.group_list = c_group_list
} }
const deleteGroup = (gIndex: number) => { const deleteGroup = (gIndex: number) => {
const c_group_list = cloneDeep(form_data.value.group_list) const c_group_list = cloneDeep(form_data.value.group_list)
c_group_list.splice(gIndex,1) c_group_list.splice(gIndex,1)
form_data.value.group_list = c_group_list form_data.value.group_list = c_group_list
const fields = c_group_list.map((item:any) => ({ label: item.group_name, value: item.group_name})) inputFieldList.value.splice(gIndex, 1)
const fields = c_group_list.map((item:any) => ({ label: item.label, value: item.field}))
set(props.nodeModel.properties.config, 'fields', fields) set(props.nodeModel.properties.config, 'fields', fields)
} }
@ -245,32 +231,22 @@ const deleteVariable = (gIndex: number,vIndex: number) => {
form_data.value.group_list = c_group_list form_data.value.group_list = c_group_list
} }
const addGroup = () => { const addGroup = (data: any) => {
let group_number = form_data.value.group_list.length + 1
let group_name = `Group${group_number}`
while (form_data.value.group_list.some((item: any) => item.group_name === group_name)) {
group_number++
group_name = `Group${group_number}`
}
const c_group_list = cloneDeep(form_data.value.group_list) const c_group_list = cloneDeep(form_data.value.group_list)
c_group_list.push({ c_group_list.push({
id: randomId(), id: randomId(),
group_name: group_name, field: data.field,
label: data.label,
variable_list: [{ variable_list: [{
v_id: randomId(), v_id: randomId(),
variable: [] variable: []
}] }]
}) })
form_data.value.group_list = c_group_list form_data.value.group_list = c_group_list
const fields = c_group_list.map((item:any) => ({ label: item.group_name, value: item.group_name}))
set(props.nodeModel.properties.config, 'fields', fields)
} }
const validate = async () => { const validate = async () => {
const validate_list = [ const validate_list = [
...nodeCascaderRef.value.map((item:any)=>item.validate()), ...nodeCascaderRef.value.map((item:any)=>item.validate()),
@ -288,7 +264,10 @@ onMounted(() => {
} }
} }
set(props.nodeModel, 'validate', validate) set(props.nodeModel, 'validate', validate)
const fields = form_data.value.group_list.map((item:any) => ({ label: item.group_name, value: item.group_name})) if (props.nodeModel.properties.node_data.group_list) {
inputFieldList.value = form_data.value.group_list.map((item:any) => ({ label: item.label, field: item.field}))
}
const fields = form_data.value.group_list.map((item: any) => ({ label: item.label, value: item.field }))
set(props.nodeModel.properties.config, 'fields', fields) set(props.nodeModel.properties.config, 'fields', fields)
}) })