fix(ui): [nocode] make dag tasks work (#9506)

This commit is contained in:
Barthélémy Ledoux
2025-06-13 09:11:08 +02:00
committed by Bart Ledoux
parent c320323371
commit cb31ef642f
11 changed files with 103 additions and 60 deletions

View File

@@ -15,19 +15,15 @@
<slot v-else />
</span>
</template>
<script>
export default {
props:{
tooltip: {
type: String,
default: ""
},
placement:{
type: String,
default: "top"
},
},
}
<script lang="ts" setup>
withDefaults(
defineProps<{
tooltip?: string;
placement?: string;
}>(),{
tooltip: "",
placement: "",
});
</script>
<style lang="scss" scoped>

View File

@@ -1,5 +1,5 @@
<template>
<div class="h-100 overflow-y-auto no-code">
<div class="no-code">
<Breadcrumbs />
<hr class="m-0">
@@ -92,4 +92,13 @@
})
</script>
<style scoped lang="scss" src="./styles/code.scss" />
<style lang="scss" scoped>
.no-code {
height: 100%;
overflow-y: auto;
hr {
margin: 0;
}
}
</style>

View File

@@ -1,19 +1,17 @@
<template>
<div>
<NoCode
:flow="lastValidFlowYaml"
:parent-path="parentPath"
:ref-path="refPath"
:block-type="blockType"
:creating-task="creatingTask"
:editing-task="editingTask"
:position
@update-metadata="(e) => onUpdateMetadata(e)"
@update-task="(e) => editorUpdate(e)"
@reorder="(yaml) => handleReorder(yaml)"
@close-task="() => emit('closeTask')"
/>
</div>
<NoCode
:flow="lastValidFlowYaml"
:parent-path="parentPath"
:ref-path="refPath"
:block-type="blockType"
:creating-task="creatingTask"
:editing-task="editingTask"
:position
@update-metadata="(e) => onUpdateMetadata(e)"
@update-task="(e) => editorUpdate(e)"
@reorder="(yaml) => handleReorder(yaml)"
@close-task="() => emit('closeTask')"
/>
</template>
<script setup lang="ts">

View File

@@ -147,6 +147,25 @@
}
function removeNullAndUndefined(obj: any): any {
if (Array.isArray(obj)) {
return obj.filter(item => item !== null && item !== undefined)
.map(item => removeNullAndUndefined(item));
}
if (typeof obj === "object") {
const newObj: any = {};
for (const key in obj) {
const rawValue = obj[key]
if(rawValue === null || rawValue === undefined) {
continue;
}
newObj[key] = removeNullAndUndefined(rawValue);
}
return newObj;
}
return obj;
}
function onTaskInput(val: PartialCodeElement | undefined) {
taskObject.value = val;
if (isPluginDefaults.value) {
@@ -165,7 +184,7 @@
};
}
}
modelValue.value = YAML_UTILS.stringify(toRaw(val));
modelValue.value = YAML_UTILS.stringify(removeNullAndUndefined(toRaw(val)));
}
function onTaskTypeSelect() {

View File

@@ -23,7 +23,7 @@
:is="componentType"
:model-value="element"
:task="modelValue"
root="array"
:root="`${root}[${index}]`"
:properties="{}"
:schema="props.schema.items"
:definitions="props.definitions"
@@ -40,7 +40,7 @@
</template>
<script setup lang="ts">
import {computed, ref} from "vue";
import {computed} from "vue";
import {DeleteOutline, ChevronUp, ChevronDown} from "../../code/utils/icons";
@@ -56,11 +56,13 @@
definitions: any;
modelValue?: (string | number | boolean | undefined)[] | string | number | boolean;
required?: boolean;
root?: string;
}>(), {
modelValue: undefined,
schema: () => ({}),
definitions: () => ({}),
required: false,
root: undefined,
});
const componentType = computed(() => {
@@ -68,47 +70,59 @@
});
const needWrapper = computed(() => {
return componentType.value.ksTaskName !== "string" &&
componentType.value.ksTaskName !== "number" &&
componentType.value.ksTaskName !== "boolean" &&
componentType.value.ksTaskName !== "expression";
return ![
"string",
"number",
"boolean",
"expression",
].includes(componentType.value.ksTaskName)
});
const items = ref(
const items = computed(() =>
props.modelValue === undefined && !props.required
// we want to avoid displaying an item completely empty when modelValue is undefined
// except if the field is required
// we want to avoid displaying an item completely empty when
// modelValue is undefined except if the field is required
? []
: !Array.isArray(props.modelValue) ? [props.modelValue] : props.modelValue,
);
const handleInput = (value: string, index: number) => {
items.value[index] = value;
emits("update:modelValue", items.value);
emits("update:modelValue", items.value.toSpliced(index, 1, value));
};
const newEmptyValue = computed(() => {
if (props.schema.items?.type === "string") {
return "";
}
return props.schema.items?.default ?? undefined;
})
const addItem = () => {
items.value.push(undefined);
emits("update:modelValue", items.value);
emits("update:modelValue", [...items.value, newEmptyValue.value]);
};
const removeItem = (index: number) => {
items.value.splice(index, 1);
emits("update:modelValue", items.value);
if (items.value.length <= 1) {
emits("update:modelValue", undefined);
return;
}
emits("update:modelValue", items.value.toSpliced(index, 1));
};
const moveItem = (index: number, direction: "up" | "down") => {
const tempValue = items.value
if (direction === "up" && index > 0) {
[items.value[index - 1], items.value[index]] = [
items.value[index],
items.value[index - 1],
[tempValue[index - 1], tempValue[index]] = [
tempValue[index],
tempValue[index - 1],
];
} else if (direction === "down" && index < items.value.length - 1) {
[items.value[index + 1], items.value[index]] = [
items.value[index],
items.value[index + 1],
} else if (direction === "down" && index < tempValue.length - 1) {
[tempValue[index + 1], tempValue[index]] = [
tempValue[index],
tempValue[index + 1],
];
}
emits("update:modelValue", items.value);
emits("update:modelValue", tempValue);
};
</script>

View File

@@ -4,6 +4,9 @@
:schema
:definitions
:properties="computedProperties"
:root="root"
:task="task"
:required="required"
merge
@update:model-value="onInput"
/>

View File

@@ -151,6 +151,7 @@
"onUpdate:modelValue": (value) => {
this.onObjectInput(key, value);
},
root: this.root,
fieldKey: key,
task: this.modelValue,
schema: schema,

View File

@@ -66,6 +66,7 @@
const props = defineProps<{
schema: any;
definitions: any;
root?: string;
fieldKey: string;
task: any;
modelValue?: Record<string, any> | string | number | boolean | Array<any>,
@@ -80,7 +81,7 @@
const taskComponent = templateRef<{resetSelectType?: () => void}>("taskComponent");
const required = computed(() => {
return props.required?.includes(props.fieldKey);
return props.required?.includes(props.fieldKey) && props.schema.$required
})
const componentProps = computed(() => {
@@ -90,7 +91,7 @@
emit("update:modelValue", value);
},
task: props.task,
root: props.fieldKey,
root: props.root ? `${props.root}.${props.fieldKey}` : props.fieldKey,
schema: props.schema,
required: required.value,
definitions: props.definitions

View File

@@ -39,7 +39,6 @@
schema-type="flow"
lang="plaintext"
input
:placeholder="`Your ${root || 'value'} here...`"
@update:model-value="onInput"
:large-suggestions="false"
/>

View File

@@ -15,7 +15,11 @@
<script setup lang="ts">
import {computed, inject} from "vue";
import {PARENT_PATH_INJECTION_KEY, REF_PATH_INJECTION_KEY, CREATING_TASK_INJECTION_KEY} from "../../code/injectionKeys";
import {
PARENT_PATH_INJECTION_KEY,
REF_PATH_INJECTION_KEY,
CREATING_TASK_INJECTION_KEY
} from "../../code/injectionKeys";
import Element from "../../code/components/collapse/Element.vue";
const model = defineModel({
@@ -24,7 +28,6 @@
});
const props = defineProps({
root: {
type: String,
required: true

View File

@@ -4,7 +4,7 @@
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable", "ES2021.String"],
"lib": ["ES2023.Array", "ES2020", "DOM", "DOM.Iterable", "ES2021.String"],
"skipLibCheck": true,
"incremental": true,
"types": ["vitest/globals"],