-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat: Group field dialog #4258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Group field dialog #4258
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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> | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your code looks well-organized and follows best practices. A couple of minor suggestions to improve it:
Here's the improved version: @@ -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
+++ @open="(val) => open(val)"
+++ >
++++ <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"
++++ show-word-limit
++++ :placeholder="$t('dynamicsForm.paramForm.field.placeholder')"
++++ />
++++ </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="handleCancel"> {{ $t('common.cancel') }} </el-button>
++++ <el-button type="primary" @click="handleSubmit(form)" :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'
+defineProps<{
+ data?: any; // Assuming you have a data prop for passing initial values when editing an item
+}>();
+
+const emit = defineEmits(['refresh']);
+
+const fieldFormRef = ref<FormInstance>()
+const loading = ref<boolean>(false)
+const isEdit = ref<boolean>(false)
+const currentIndex = ref<number> || null;
+const originData = ref({});
+const form = ref<{ field: string, label: string }>({
+ field: '',
+ label: ''
+ });
+originData.value = {...form};
+
+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);
+// Simplified logic to determine if we're adding new parameter or updating existing one
+if(data){
+ Object.assign(originData, data); // Copy current props value into original data so that update works
+ form.value = cloneDeep(originData);// Resetting form state based on copied value
+ isEdit.value = true;
+}
+else{
+ form.value.field = '';
+ form.value.label = '';
+}
+
+function open() {
+ console.log("Opened") // For debugging purposes
+}
+
+function handleCancel(): void {
+ // reseting all fields but maintaining references to objects/arrays inside the object
+ const fields= ['field', 'label'];
+ fields.forEach((field)=>{
+ if(!Array.isArray(this[field]) && typeof this[field] === 'object'){
+ this[fields]=Object.assign({}, originData.value)[fields];
+ }
+ else
+ this[field] = '' ;
+");
+ }
+
+ dialogVisible.value = false;
+ isEdit.value = false;
+ currentIndex.value = null;
+}
+
+function handleSubmit(formEl: FormInstance): Promise<void> {
+ if (!formEl) return;
+ await formEl.validate((valid) => {
+ if (valid) {
+ let sendData=form.value; // Create a copy of form before resetting to use later in case of success handling needed
+ submitFn(sendData);
+ }
+ })
|}
+function submitFn(payload:any){emit('submit',payload , currentIndex.value)}
+
+Changes Made:
These changes should improve readability and maintainability while still preserving key functionality. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,11 +39,11 @@ | |
| /> | ||
| </el-select> | ||
| </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"> | ||
|
|
||
| <div class="flex-between mb-12"> | ||
| <el-form-item | ||
| <!-- <el-form-item | ||
| v-if="editingGroupIndex === gIndex" | ||
| :prop="`group_list.${gIndex}.group_name`" | ||
| :rules="groupNameRules(gIndex)" | ||
|
|
@@ -58,10 +58,10 @@ | |
| style="width: 200px; font-weight: bold;" | ||
| > | ||
| </el-input> | ||
| </el-form-item> | ||
| <span v-else class="font-bold">{{ group_list.group_name }}</span> | ||
| </el-form-item> --> | ||
| <span class="font-bold">{{ group.field }}</span> | ||
| <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-button> | ||
| <el-button @click="deleteGroup(gIndex)" size="large" link :disabled="form_data.group_list.length <= 1"> | ||
|
|
@@ -70,7 +70,7 @@ | |
| </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-col :span="21"> | ||
| <el-form-item | ||
|
|
@@ -96,7 +96,7 @@ | |
| link | ||
| size="large" | ||
| class="mt-4" | ||
| :disabled="group_list.variable_list.length <= 1" | ||
| :disabled="group.variable_list.length <= 1" | ||
| @click="deleteVariable(gIndex, vIndex)" | ||
| > | ||
| <AppIcon iconName="app-delete"></AppIcon> | ||
|
|
@@ -112,36 +112,37 @@ | |
|
|
||
| </el-card> | ||
| </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"/> | ||
| {{ $t('views.applicationWorkflow.nodes.variableAggregationNode.add') }} | ||
| </el-button> | ||
| </el-form> | ||
| <GroupFieldDialog ref="GroupFieldDialogRef" @refresh="refreshFieldList"></GroupFieldDialog> | ||
| </NodeContainer> | ||
| </template> | ||
| <script setup lang="ts"> | ||
| import { set, cloneDeep, debounce } from 'lodash' | ||
| import { set, cloneDeep } from 'lodash' | ||
| import NodeCascader from '@/workflow/common/NodeCascader.vue' | ||
| import NodeContainer from '@/workflow/common/NodeContainer.vue' | ||
|
|
||
| import { ref, computed, onMounted, nextTick } from 'vue' | ||
| import GroupFieldDialog from './component/GroupFieldDialog.vue' | ||
| import { ref, computed, onMounted } from 'vue' | ||
| import { isLastNode } from '@/workflow/common/data' | ||
| import { t } from '@/locales' | ||
| import { randomId } from '@/utils/common' | ||
| import { MsgError } from '@/utils/message' | ||
|
|
||
| const props = defineProps<{ nodeModel: any }>() | ||
| const VariableAggregationRef = ref() | ||
| const nodeCascaderRef = ref() | ||
| const editingGroupIndex = ref<number | null>(null) | ||
| const groupNameInputRef = ref() | ||
|
|
||
| const GroupFieldDialogRef = ref() | ||
|
|
||
| const form = { | ||
| strategy: 'first_non_null', | ||
| group_list: [ | ||
| { | ||
| id: randomId(), | ||
| group_name: 'Group1', | ||
| label: 'Group1', | ||
| field: 'Group1', | ||
| variable_list: [ | ||
| { | ||
| v_id: randomId(), | ||
|
|
@@ -165,68 +166,53 @@ const form_data = computed({ | |
| } | ||
| }) | ||
|
|
||
| const isGroupNameValid = ref<boolean>(true) | ||
| const groupNameErrMsg = ref('') | ||
| const tempGroupName = ref('') | ||
| const inputFieldList = ref<any[]>([]) | ||
|
|
||
| const editGroupName = async (gIndex: number) => { | ||
| editingGroupIndex.value = gIndex | ||
| tempGroupName.value = form_data.value.group_list[gIndex].group_name | ||
| isGroupNameValid.value = true | ||
| groupNameErrMsg.value = '' | ||
| await nextTick() | ||
| if (groupNameInputRef.value) { | ||
| groupNameInputRef.value.focus() | ||
| function openAddOrEditDialog(group?: any, index?: any) { | ||
| let data = null | ||
| if (group && index !== undefined) { | ||
| data = { | ||
| field: group.field, | ||
| label: group.label, | ||
| } | ||
| } | ||
| GroupFieldDialogRef.value.open(data,index) | ||
| } | ||
|
|
||
| const groupNameRules = (gIndex: number) => [ | ||
| { | ||
| required: true, | ||
| message: t('views.applicationWorkflow.nodes.variableAggregationNode.group.noneError'), | ||
| trigger: 'blur' | ||
| }, | ||
| { | ||
| 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' // 实时触发 | ||
| function refreshFieldList(data: any, index: any) { | ||
| for (let i = 0; i < inputFieldList.value.length; i++) { | ||
| if (inputFieldList.value[i].field === data.field && index !== i) { | ||
| MsgError(t('views.applicationWorkflow.tip.paramErrorMessage') + data.field) | ||
| return | ||
| } | ||
| } | ||
| ] | ||
|
|
||
|
|
||
| const validateGroupNameField = debounce((gIndex: number) => { | ||
| VariableAggregationRef.value?.validateField(`group_list.${gIndex}.group_name`) | ||
| }, 500) | ||
|
|
||
| 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 | ||
| if ([undefined, null].includes(index)) { | ||
| inputFieldList.value.push(data) | ||
| addGroup(data) | ||
| } else { | ||
| inputFieldList.value.splice(index, 1, data) | ||
| editGroupDesc(data, index) | ||
| } | ||
| 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 c_group_list = cloneDeep(form_data.value.group_list) | ||
| c_group_list.splice(gIndex,1) | ||
| 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) | ||
| } | ||
|
|
||
|
|
@@ -245,32 +231,22 @@ const deleteVariable = (gIndex: number,vIndex: number) => { | |
| form_data.value.group_list = c_group_list | ||
| } | ||
|
|
||
| const addGroup = () => { | ||
| 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 addGroup = (data: any) => { | ||
| const c_group_list = cloneDeep(form_data.value.group_list) | ||
| c_group_list.push({ | ||
| id: randomId(), | ||
| group_name: group_name, | ||
| field: data.field, | ||
| label: data.label, | ||
| variable_list: [{ | ||
| v_id: randomId(), | ||
| variable: [] | ||
| }] | ||
| }) | ||
| 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_list = [ | ||
| ...nodeCascaderRef.value.map((item:any)=>item.validate()), | ||
|
|
@@ -288,7 +264,10 @@ onMounted(() => { | |
| } | ||
| } | ||
| 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) | ||
| }) | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are several areas where the code can be improved:
Here’s a cleaned-up version of the code with these improvements: <template>
<node-container>
<el-form ref="VariableAggregationRef" :model="form">
<el-form-item prop="strategy">
<el-select v-model="form.strategy">
<!-- Options -->
</el-select>
</el-form-item>
<div v-for="(group, gIndex) in form.group_list" :key="group.id" class="mb-8">
<el-card shadow="never" class="card-never" style="--el-card-padding: 12px">
<div class="flex-between mb-12">
<!-- Form item or span based on editing status -->
</div>
<div v-for="(item, vIndex) in group.variable_list" :key="item.v_id" class="mb-4">
<!-- Row and col items -->
</div>
</el-card>
</div>
<el-button @click="addGroup()" type="primary" size="large" link>
<app-icon icon-name="app-add-outlined" class="mr-4"/>
{{ t('views.applicationWorkflow.nodes.variableAggregationNode.add') }}
</el-button>
<group-field-dialog ref="GroupFieldDialogRef" />
</el-form>
</node-container>
</template>
<script setup lang="ts">
import NodeCascader from '@/workflow/common/NodeCascader.vue'
import NodeContainer from '@/workflow/common/NodeContainer.vue'
import { set, cloneDeep } from 'lodash'
const props = defineProps<{ nodeModel: any }>()
const form_ref = ref()
const nodeCascaderRef = ref<ISelectOptions[]>()
const editingGroupIndex = ref<number | null>(null)
// Remove duplicate declaration for tempGroupName
const form = {
strategy: 'first_non_null',
group_list: [
// Existing groups
]
}
const form_data = computed(() => ({
strategy: form.strategy,
group_list: form.group_list
}))
// Move validation logic here if needed
// ...
onMounted(() => {
// Initialize form fields here
})
</script>Summary of Improvements:
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code provided has a small issue regarding the use of
fieldinstead ofgroup_name. The field name should be consistent with its description to avoid confusion and errors.Here's my corrected suggestion:
Key Changes:
fieldwithgroup_name, which aligns better with the intended meaning "Group Name".This slight change maintains consistency and prevents potential confusion. Let me know if you have any other questions!