业务画面:<template>
<div>
<CustomForm :form-data="formData" :rules="rules" @submit="handleSubmit">
<template #default>
<CustomFormItem label="产品名称" prop="name" :required="true">
<CustomInput v-model="formData.name" placeholder="请输入产品名称"/>
</CustomFormItem>
<CustomFormItem label="产品描述" prop="description" :required="true">
<CustomInput v-model="formData.description" placeholder="请输入产品描述" type="textarea"/>
</CustomFormItem>
</template>
</CustomForm>
<div>产品名称值: {{ formData.name }}</div>
<div>产品描述值: {{ formData.description }}</div>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import CustomForm from '@/components/customForm';
import CustomFormItem from '@/components/customFormItem';
import CustomInput from '@/components/customInput';
const formData = reactive({
name: '',
description: ''
});
const rules = reactive({
name: [
{ required: true, message: '产品名称不能为空' },
{ max: 10, message: '长度超过10个汉字' }
],
description: [
{ required: true, message: '产品描述不能为空' },
{ max: 200, message: '描述不能超过200个汉字' }
]
});
function handleSubmit(valid, data) {
if (valid) {
alert('表单验证通过! 提交数据: ' + JSON.stringify(data));
} else {
console.log('表单验证失败');
}
}
</script> 组件customForm:<template>
<form @submit.prevent="submitForm" class="custom-form">
<slot name="default"></slot>
<button type="submit" class="submit-btn">
提交
</button>
</form>
</template>
<script setup>
import { ref, defineProps, defineEmits, provide } from 'vue';
// 添加依赖注入
provide('formData', props.formData);
provide('formRules', props.rules);
const props = defineProps({
formData: {
type: Object,
required: true
},
rules: {
type: Object,
default: () => ({})
}
});
const emit = defineEmits(['submit']);
const formItems = ref([]);
// 向子组件提供注册方法
provide('registerFormItem', (item) => {
formItems.value.push(item);
});
async function submitForm() {
let isValid = true;
// 验证所有表单项
for (const item of formItems.value) {
const valid = await item.validate();
if (!valid) isValid = false;
}
emit('submit', isValid, props.formData);
}
</script>
<style scoped>
.custom-form {
margin-bottom: 20px;
display: flex;
flex-direction: column;
gap: 18px;
}
.submit-btn {
margin-top: 15px;
padding: 10px 20px;
background-color: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
align-self: flex-start;
}
.submit-btn:hover {
background-color: #66b1ff;
}
</style>
组件customFormItem:<template>
<div class="custom-form-item">
<label v-if="label" class="form-label">
{{ label }}
<span v-if="required" class="required-mark">*</span>
</label>
<div class="form-content">
<slot></slot>
<div v-if="errorMessage" class="error-message">{{ errorMessage }}</div>
</div>
</div>
</template>
<script setup>
import { ref, defineProps, inject, onMounted, watch } from 'vue';
const props = defineProps({
label: String,
prop: String,
required: Boolean
});
const formData = inject('formData');
const formRules = inject('formRules');
const registerFormItem = inject('registerFormItem');
const errorMessage = ref('');
const currentValue = ref('');
// 注册到父表单
onMounted(() => {
if (registerFormItem && props.prop) {
registerFormItem({
prop: props.prop,
validate
});
}
});
// 值变化监听
watch(() => formData[props.prop], (newVal) => {
currentValue.value = newVal;
validate();
});
function validate() {
if (!props.prop || !formRules || !formRules[props.prop]) {
errorMessage.value = '';
return true;
}
const rules = formRules[props.prop];
const value = currentValue.value;
for (const rule of rules) {
if (rule.required && !value?.trim()) {
errorMessage.value = rule.message;
return false;
}
if (rule.max && value?.length > rule.max) {
errorMessage.value = rule.message;
return false;
}
}
errorMessage.value = '';
return true;
}
</script>
<style scoped>
.custom-form-item {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-label {
font-weight: 600;
font-size: 14px;
}
.required-mark {
color: #f56c6c;
margin-left: 4px;
}
.form-content {
display: flex;
flex-direction: column;
gap: 4px;
}
.error-message {
color: #f56c6c;
font-size: 12px;
height: 20px;
}
</style>组件customInput:<template>
<div class="custom-input">
<input
v-if="type !== 'textarea'" :value="modelValue" @input="handleInput" @blur="$emit('blur')" :placeholder="placeholder" :type="type" class="input-field"/>
<textarea v-else :value="modelValue" @input="handleInput" @blur="$emit('blur')" :placeholder="placeholder" class="textarea-field"></textarea>
</div>
</template>
<script setup>
defineProps({
modelValue: [String, Number],
placeholder: {
type: String,
default: ""
},
type: {
type: String,
default: "text"
}
});
const emit = defineEmits(['update:modelValue', 'blur']);
function handleInput(event) {
emit('update:modelValue', event.target.value);
}
</script>
<style scoped>
.custom-input {
width: 100%;
}
.input-field, .textarea-field {
width: 100%;
padding: 10px 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.3s;
}
.textarea-field {
min-height: 80px;
resize: vertical;
}
.input-field:focus,
.textarea-field:focus {
border-color: #409eff;
outline: none;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
</style> 哪写错啦,不显示
最新发布