113 lines
No EOL
2.2 KiB
Vue
113 lines
No EOL
2.2 KiB
Vue
<template>
|
|
<form @submit.prevent="handleSubmit" class="form-container">
|
|
<div v-for="(field, index) in fields" :key="index" class="form-field">
|
|
<Input
|
|
:id="field.id"
|
|
:label="field.label"
|
|
:type="field.type"
|
|
v-model="formData[field.id]"
|
|
:error="errors[field.id]"
|
|
@blur="validateField(field.id)"
|
|
v-bind="field.attrs"
|
|
/>
|
|
</div>
|
|
<slot name="submit">
|
|
<button v-if="showSubmit" type="submit" class="submit-button">Submit</button>
|
|
</slot>
|
|
</form>
|
|
</template>
|
|
|
|
<script setup>
|
|
|
|
const props = defineProps({
|
|
fields: {
|
|
type: Array,
|
|
required: true,
|
|
validator: (value) => {
|
|
return value.every(field => field.id && field.label)
|
|
}
|
|
},
|
|
showSubmit: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
validationRules: {
|
|
type: Object,
|
|
default: () => ({})
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['submit'])
|
|
|
|
const formData = reactive({})
|
|
const errors = reactive({})
|
|
|
|
props.fields.forEach(field => {
|
|
formData[field.id] = field.defaultValue || ''
|
|
errors[field.id] = ''
|
|
})
|
|
|
|
const validateField = (fieldId) => {
|
|
const rules = props.validationRules[fieldId]
|
|
if (!rules) return
|
|
|
|
const value = formData[fieldId]
|
|
errors[fieldId] = ''
|
|
|
|
if (rules.required && !value) {
|
|
errors[fieldId] = 'This field is required'
|
|
return
|
|
}
|
|
|
|
if (rules.minLength && value.length < rules.minLength) {
|
|
errors[fieldId] = `Minimum ${rules.minLength} characters required`
|
|
return
|
|
}
|
|
|
|
if (rules.pattern && !new RegExp(rules.pattern).test(value)) {
|
|
errors[fieldId] = rules.patternMessage || 'Invalid format'
|
|
}
|
|
}
|
|
|
|
const validateAllFields = () => {
|
|
let isValid = true
|
|
props.fields.forEach(field => {
|
|
validateField(field.id)
|
|
if (errors[field.id]) isValid = false
|
|
})
|
|
return isValid
|
|
}
|
|
|
|
const handleSubmit = () => {
|
|
if (validateAllFields()) {
|
|
emit('submit', formData)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.form-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.form-field {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.submit-button {
|
|
padding: 0.75rem 1.5rem;
|
|
background-color: #42b983;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
font-size: 1rem;
|
|
cursor: pointer;
|
|
align-self: flex-start;
|
|
}
|
|
|
|
.submit-button:hover {
|
|
background-color: #3aa876;
|
|
}
|
|
</style> |