197 lines
8.1 KiB
JavaScript
197 lines
8.1 KiB
JavaScript
export const DynamicForm = {
|
|
name: 'DynamicForm',
|
|
props: {
|
|
formData: {
|
|
type: Object,
|
|
required: true,
|
|
validator: (formData) => {
|
|
return formData && formData.title && formData.fields &&
|
|
(Array.isArray(formData.fields) || typeof formData.fields === 'object');
|
|
}
|
|
},
|
|
formValues: {
|
|
type: Object,
|
|
default: () => ({})
|
|
},
|
|
isSubmitting: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
readOnly: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
hideActions: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
},
|
|
emits: ['submit', 'cancel', 'update:formValues'],
|
|
data() {
|
|
return {
|
|
localFormValues: { ...this.formValues }
|
|
};
|
|
},
|
|
watch: {
|
|
formValues: {
|
|
handler(newValues) {
|
|
this.localFormValues = { ...newValues };
|
|
},
|
|
deep: true
|
|
},
|
|
localFormValues: {
|
|
handler(newValues) {
|
|
this.$emit('update:formValues', newValues);
|
|
},
|
|
deep: true
|
|
}
|
|
},
|
|
methods: {
|
|
handleSubmit() {
|
|
// Basic validation
|
|
const missingFields = [];
|
|
|
|
if (Array.isArray(this.formData.fields)) {
|
|
// Valideer array-gebaseerde velden
|
|
this.formData.fields.forEach(field => {
|
|
const fieldId = field.id || field.name;
|
|
if (field.required) {
|
|
const value = this.localFormValues[fieldId];
|
|
if (value === undefined || value === null ||
|
|
(typeof value === 'string' && !value.trim()) ||
|
|
(Array.isArray(value) && value.length === 0)) {
|
|
missingFields.push(field.name);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
// Valideer object-gebaseerde velden
|
|
Object.entries(this.formData.fields).forEach(([fieldId, field]) => {
|
|
if (field.required) {
|
|
const value = this.localFormValues[fieldId];
|
|
if (value === undefined || value === null ||
|
|
(typeof value === 'string' && !value.trim()) ||
|
|
(Array.isArray(value) && value.length === 0)) {
|
|
missingFields.push(field.name);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
if (missingFields.length > 0) {
|
|
const fieldNames = missingFields.join(', ');
|
|
alert(`De volgende velden zijn verplicht: ${fieldNames}`);
|
|
return;
|
|
}
|
|
|
|
this.$emit('submit', this.localFormValues);
|
|
},
|
|
|
|
handleCancel() {
|
|
this.$emit('cancel');
|
|
},
|
|
|
|
updateFieldValue(fieldId, value) {
|
|
this.localFormValues[fieldId] = value;
|
|
}
|
|
},
|
|
template: `
|
|
<div class="dynamic-form" :class="{ 'read-only': readOnly }">
|
|
<div class="form-header" v-if="formData.title || formData.icon">
|
|
<div class="form-icon" v-if="formData.icon">
|
|
<i class="material-icons">{{ formData.icon }}</i>
|
|
</div>
|
|
<div class="form-title">{{ formData.title }}</div>
|
|
</div>
|
|
|
|
<div v-if="readOnly" class="form-readonly">
|
|
<!-- Array-based fields -->
|
|
<template v-if="Array.isArray(formData.fields)">
|
|
<div v-for="field in formData.fields" :key="field.id || field.name" class="form-field-readonly">
|
|
<div class="field-label">{{ field.name }}:</div>
|
|
<div class="field-value">
|
|
<template v-if="field.type === 'enum' && (field.allowedValues || field.allowed_values)">
|
|
{{ localFormValues[field.id || field.name] || field.default || '-' }}
|
|
</template>
|
|
<template v-else-if="field.type === 'boolean'">
|
|
{{ localFormValues[field.id || field.name] ? 'Ja' : 'Nee' }}
|
|
</template>
|
|
<template v-else-if="field.type === 'text'">
|
|
<div class="text-value">{{ localFormValues[field.id || field.name] || '-' }}</div>
|
|
</template>
|
|
<template v-else>
|
|
{{ localFormValues[field.id || field.name] || field.default || '-' }}
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<!-- Object-based fields -->
|
|
<template v-else>
|
|
<div v-for="(field, fieldId) in formData.fields" :key="fieldId" class="form-field-readonly">
|
|
<div class="field-label">{{ field.name }}:</div>
|
|
<div class="field-value">
|
|
<template v-if="field.type === 'enum' && (field.allowedValues || field.allowed_values)">
|
|
{{ localFormValues[fieldId] || field.default || '-' }}
|
|
</template>
|
|
<template v-else-if="field.type === 'boolean'">
|
|
{{ localFormValues[fieldId] ? 'Ja' : 'Nee' }}
|
|
</template>
|
|
<template v-else-if="field.type === 'text'">
|
|
<div class="text-value">{{ localFormValues[fieldId] || '-' }}</div>
|
|
</template>
|
|
<template v-else>
|
|
{{ localFormValues[fieldId] || field.default || '-' }}
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<form v-else @submit.prevent="handleSubmit" novalidate>
|
|
<div class="form-fields">
|
|
<template v-if="Array.isArray(formData.fields)">
|
|
<form-field
|
|
v-for="field in formData.fields"
|
|
:key="field.id || field.name"
|
|
:field-id="field.id || field.name"
|
|
:field="field"
|
|
:model-value="localFormValues[field.id || field.name]"
|
|
@update:model-value="localFormValues[field.id || field.name] = $event"
|
|
></form-field>
|
|
</template>
|
|
<template v-else>
|
|
<form-field
|
|
v-for="(field, fieldId) in formData.fields"
|
|
:key="fieldId"
|
|
:field-id="fieldId"
|
|
:field="field"
|
|
:model-value="localFormValues[fieldId]"
|
|
@update:model-value="localFormValues[fieldId] = $event"
|
|
></form-field>
|
|
</template>
|
|
</div>
|
|
|
|
<div class="form-actions" v-if="!hideActions">
|
|
<button
|
|
type="submit"
|
|
class="btn btn-primary"
|
|
:disabled="isSubmitting"
|
|
:class="{ 'loading': isSubmitting }"
|
|
>
|
|
<span v-if="isSubmitting" class="spinner"></span>
|
|
{{ isSubmitting ? 'Verzenden...' : (formData.submitText || 'Versturen') }}
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
class="btn btn-secondary"
|
|
@click="handleCancel"
|
|
:disabled="isSubmitting"
|
|
>
|
|
{{ formData.cancelText || 'Annuleren' }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
`
|
|
}; |