feat(nodeEdit): Add node configurator and schema field components

- Introduced the NodeConfigurator component for node configuration
- Added the SchemaField component for schema field rendering
- Changed node parameters to an object configuration model
- Added error handling and notification mechanisms
- Modified the CoreTypes definition method to avoid Proxy issues
This commit is contained in:
2025-12-26 17:27:36 +08:00
parent b085ba3c60
commit 57f29f855b
8 changed files with 236 additions and 31 deletions

View File

@@ -0,0 +1,132 @@
<script setup lang="ts">
import {computed, toRaw} from 'vue';
import { z, ZodObject, ZodString, ZodNumber, ZodBoolean, ZodEnum, ZodOptional } from 'zod';
defineOptions({
name: 'SchemaField'
})
const props = defineProps<{
schema: z.ZodTypeAny;
modelValue: any;
label: string;
}>();
// 使用toRaw获取原始schema对象避免Proxy问题
const rawSchema = toRaw(props.schema);
const emit = defineEmits(['update:modelValue']);
const isOptional = computed(() => rawSchema.optional());
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val),
});
const typeName = computed(() => {
if (rawSchema instanceof ZodObject) return 'ZodObject';
if (rawSchema instanceof ZodString) return 'ZodString';
if (rawSchema instanceof ZodNumber) return 'ZodNumber';
if (rawSchema instanceof ZodBoolean) return 'ZodBoolean';
if (rawSchema instanceof ZodEnum) return 'ZodEnum';
if (rawSchema instanceof ZodOptional) return 'ZodOptional';
// Fallback for unknown types or types not handled explicitly
return 'Unknown';
});
const objectValue = computed(() => value.value as Record<string, any> | null);
const objectSchema = computed(() => {
const schema = rawSchema;
if (schema instanceof ZodOptional) {
const unwrapped = schema.unwrap();
if (unwrapped instanceof ZodObject) {
return unwrapped;
}
}
if (schema instanceof ZodObject) {
return schema;
}
return null;
});
</script>
<template>
<div class="schema-field">
<template v-if="typeName === 'ZodString'">
<v-text-field
v-model="value"
:label="label"
:hint="isOptional ? 'Optional' : ''"
persistent-hint
></v-text-field>
</template>
<template v-else-if="typeName === 'ZodNumber'">
<v-text-field
v-model.number="value"
type="number"
:label="label"
:hint="isOptional ? 'Optional' : ''"
persistent-hint
></v-text-field>
</template>
<template v-else-if="typeName === 'ZodBoolean'">
<v-switch
v-model="value"
:label="label"
color="primary"
:hint="isOptional ? 'Optional' : ''"
persistent-hint
></v-switch>
</template>
<template v-else-if="typeName === 'ZodEnum'">
<v-select
v-model="value"
:items="(rawSchema as ZodEnum<any>).options"
:label="label"
:hint="isOptional ? 'Optional' : ''"
persistent-hint
></v-select>
</template>
<template v-else-if="objectSchema">
<v-card variant="outlined" class="pa-4 mb-4">
<div class="text-subtitle-1 font-weight-medium">{{ label }}</div>
<div v-for="(field, key) in objectSchema.shape" :key="key">
<SchemaField
:schema="field"
:label="key.toString()"
:model-value="objectValue ? objectValue[key] : undefined"
@update:modelValue="newValue => {
const newObjectValue = { ...value.value };
newObjectValue[key] = newValue;
value.value = newObjectValue;
}"
/>
</div>
</v-card>
</template>
<template v-else-if="typeName === 'ZodOptional'">
<SchemaField
:schema="(rawSchema as ZodOptional<any>).unwrap()"
v-model="value"
:label="label"
/>
</template>
<template v-else>
<p class="text-caption">Unsupported type: {{ typeName }} for {{label}}</p>
</template>
</div>
</template>
<style scoped>
.schema-field {
margin-bottom: 1rem;
}
</style>