- introductie van vue files - bijna werkende versie van eveai_chat_client.
This commit is contained in:
328
eveai_chat_client/static/assets/vue-components/FormField.vue
Normal file
328
eveai_chat_client/static/assets/vue-components/FormField.vue
Normal file
@@ -0,0 +1,328 @@
|
||||
<template>
|
||||
<div class="form-field" style="margin-bottom: 15px; display: grid; grid-template-columns: 35% 65%; align-items: center;">
|
||||
<!-- Label voor alle velden behalve boolean/checkbox die een speciale behandeling krijgen -->
|
||||
<label v-if="fieldType !== 'checkbox'" :for="fieldId" style="margin-right: 10px; font-weight: 500;">
|
||||
{{ field.name }}
|
||||
<span v-if="field.required" class="required" style="color: #d93025; margin-left: 2px;">*</span>
|
||||
</label>
|
||||
|
||||
<!-- Container voor input velden -->
|
||||
<div style="width: 100%;">
|
||||
<!-- Context informatie indien aanwezig -->
|
||||
<div v-if="field.context" class="field-context" style="margin-bottom: 8px; font-size: 0.9em; color: #666; background-color: #f8f9fa; padding: 8px; border-radius: 4px; border-left: 3px solid #4285f4;">
|
||||
{{ field.context }}
|
||||
</div>
|
||||
|
||||
<!-- Tekstinvoer (string/str) -->
|
||||
<input
|
||||
v-if="fieldType === 'text'"
|
||||
:id="fieldId"
|
||||
type="text"
|
||||
v-model="value"
|
||||
:required="field.required"
|
||||
:placeholder="field.placeholder || ''"
|
||||
:title="description"
|
||||
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||
>
|
||||
|
||||
<!-- Numerieke invoer (int/float) -->
|
||||
<input
|
||||
v-if="fieldType === 'number'"
|
||||
:id="fieldId"
|
||||
type="number"
|
||||
v-model.number="value"
|
||||
:required="field.required"
|
||||
:step="stepValue"
|
||||
:placeholder="field.placeholder || ''"
|
||||
:title="description"
|
||||
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||
>
|
||||
|
||||
<!-- Tekstvlak (text) -->
|
||||
<textarea
|
||||
v-if="fieldType === 'textarea'"
|
||||
:id="fieldId"
|
||||
v-model="value"
|
||||
:required="field.required"
|
||||
:rows="field.rows || 3"
|
||||
:placeholder="field.placeholder || ''"
|
||||
:title="description"
|
||||
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box; resize: vertical;"
|
||||
></textarea>
|
||||
|
||||
<!-- Selectielijst (enum) -->
|
||||
<select
|
||||
v-if="fieldType === 'select'"
|
||||
:id="fieldId"
|
||||
v-model="value"
|
||||
:required="field.required"
|
||||
:title="description"
|
||||
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||
>
|
||||
<option value="">Selecteer een optie</option>
|
||||
<option
|
||||
v-for="option in (field.allowedValues || field.allowed_values || [])"
|
||||
:key="option"
|
||||
:value="option"
|
||||
>
|
||||
{{ option }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<!-- Radio buttons (options) -->
|
||||
<div v-if="fieldType === 'radio'" class="radio-group" style="display: flex; flex-direction: column; gap: 8px;">
|
||||
<label
|
||||
v-for="option in (field.allowedValues || field.allowed_values || [])"
|
||||
:key="option"
|
||||
class="radio-label"
|
||||
style="display: flex; align-items: center; cursor: pointer;"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
:name="fieldId"
|
||||
:value="option"
|
||||
v-model="value"
|
||||
:required="field.required"
|
||||
style="margin-right: 8px;"
|
||||
>
|
||||
<span>{{ option }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Checkbox (boolean) -->
|
||||
<div v-if="fieldType === 'checkbox'" class="checkbox-container" style="display: flex; align-items: center;">
|
||||
<label class="checkbox-label" style="display: flex; align-items: center; cursor: pointer;">
|
||||
<input
|
||||
type="checkbox"
|
||||
:id="fieldId"
|
||||
v-model="value"
|
||||
:required="field.required"
|
||||
style="margin-right: 8px;"
|
||||
>
|
||||
<span class="checkbox-text">{{ field.name }}</span>
|
||||
<span v-if="field.required" class="required" style="color: #d93025; margin-left: 2px;">*</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Bestandsupload -->
|
||||
<input
|
||||
v-if="fieldType === 'file'"
|
||||
:id="fieldId"
|
||||
type="file"
|
||||
@change="handleFileUpload"
|
||||
:required="field.required"
|
||||
:accept="field.accept || '*'"
|
||||
:title="description"
|
||||
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||
>
|
||||
|
||||
<!-- Datum invoer -->
|
||||
<input
|
||||
v-if="fieldType === 'date'"
|
||||
:id="fieldId"
|
||||
type="date"
|
||||
v-model="value"
|
||||
:required="field.required"
|
||||
:title="description"
|
||||
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||
>
|
||||
|
||||
<!-- Tijd invoer -->
|
||||
<input
|
||||
v-if="fieldType === 'time'"
|
||||
:id="fieldId"
|
||||
type="time"
|
||||
v-model="value"
|
||||
:required="field.required"
|
||||
:title="description"
|
||||
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||
>
|
||||
|
||||
<!-- Email invoer -->
|
||||
<input
|
||||
v-if="fieldType === 'email'"
|
||||
:id="fieldId"
|
||||
type="email"
|
||||
v-model="value"
|
||||
:required="field.required"
|
||||
:placeholder="field.placeholder || ''"
|
||||
:title="description"
|
||||
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||
>
|
||||
|
||||
<!-- URL invoer -->
|
||||
<input
|
||||
v-if="fieldType === 'url'"
|
||||
:id="fieldId"
|
||||
type="url"
|
||||
v-model="value"
|
||||
:required="field.required"
|
||||
:placeholder="field.placeholder || ''"
|
||||
:title="description"
|
||||
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||
>
|
||||
|
||||
<!-- Beschrijving/help tekst -->
|
||||
<small v-if="description" class="field-description" style="display: block; margin-top: 5px; font-size: 0.8rem; color: #777; line-height: 1.4;">
|
||||
{{ description }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FormField',
|
||||
props: {
|
||||
field: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator: (field) => {
|
||||
return field.name && field.type;
|
||||
}
|
||||
},
|
||||
fieldId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
modelValue: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
computed: {
|
||||
value: {
|
||||
get() {
|
||||
// Gebruik default waarde als modelValue undefined is
|
||||
if (this.modelValue === undefined || this.modelValue === null) {
|
||||
if (this.field.type === 'boolean') {
|
||||
return this.field.default === true;
|
||||
}
|
||||
return this.field.default !== undefined ? this.field.default : '';
|
||||
}
|
||||
return this.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
// Voorkom emit als de waarde niet is veranderd
|
||||
if (JSON.stringify(value) !== JSON.stringify(this.modelValue)) {
|
||||
this.$emit('update:modelValue', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
fieldType() {
|
||||
// Map Python types naar HTML input types
|
||||
const typeMap = {
|
||||
'str': 'text',
|
||||
'string': 'text',
|
||||
'int': 'number',
|
||||
'integer': 'number',
|
||||
'float': 'number',
|
||||
'text': 'textarea',
|
||||
'enum': 'select',
|
||||
'options': 'radio',
|
||||
'boolean': 'checkbox',
|
||||
'file': 'file',
|
||||
'date': 'date',
|
||||
'time': 'time',
|
||||
'email': 'email',
|
||||
'url': 'url'
|
||||
};
|
||||
return typeMap[this.field.type] || this.field.type;
|
||||
},
|
||||
stepValue() {
|
||||
return this.field.type === 'float' ? 'any' : 1;
|
||||
},
|
||||
description() {
|
||||
return this.field.description || '';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleFileUpload(event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
this.value = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-field {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-field input:focus,
|
||||
.form-field select:focus,
|
||||
.form-field textarea:focus {
|
||||
outline: none;
|
||||
border-color: #4a90e2 !important;
|
||||
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.radio-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-text {
|
||||
font-size: 0.9rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.field-description {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
font-size: 0.8rem;
|
||||
color: #777;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.field-context {
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
background-color: #f8f9fa;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #4285f4;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #d93025;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.form-field {
|
||||
display: block !important;
|
||||
grid-template-columns: none !important;
|
||||
}
|
||||
|
||||
.form-field label {
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user