vue2 封装组件使用 v-mode【el-radio,el-input】
v-model 在组件上使用,只能更改一个值。
sycn [singk]
1、在组件上使用 v-model ,父组件 v-model ,子组件接收value , $emit('value',xxxx)
2、sync [singk]. 父组件 :message属性值.sync ='xxxxx' ,,子组件。 $emit('update:message属性值',xxxxx)
父组件
<!--- 1、在组件上使用 v-model ,父组件 v-model ,子组件接收value , $emit('value',xxxx) 2、sync [singk]. 父组件 :message属性值.sync ='xxxxx' ,,子组件。 $emit('update:message属性值',xxxxx) --> <template> <el-form ref="formRef" :model="formData" label-width="100px" style="margin: 20px; border: 1px solid red" > <!-- <page-one v-model="showflag" :message.sync="msg"></page-one> --> <!-- v-model, sync --> <!-- 使用封装的单选组件 --> <el-form-item label="性别" prop="gender" :rules="genderRules"> <radio-group v-model="formData.gender" :options="genderOptions" /> </el-form-item> <el-form-item label="学历" prop="education" :rules="educationRules"> <radio-group v-model="formData.education" :options="educationOptions"> <!-- 自定义选项内容(插槽示例) --> <template #default="{ option }"> <template v-if="option.label === '本科'"> <span style="color: #1890ff">{{ option.label }} 👶</span> </template> </template> </radio-group> </el-form-item> <el-form-item label="兴趣爱好" prop="hobbies" :rules="checkboxRules"> <custom-checkbox v-model="formData.hobbies" :options="checkboxlistopts" :min="1" :max="2" /> </el-form-item> <el-form-item label="输入框" prop="inputmsg" :rules="inputmsgRules"> <customInput v-model="formData.inputmsg" :maxlength="11" show-word-limit placeholder="请输入用户名" clearable :inputStyle="{'width':'200px'}" :prefixIcon="'el-icon-search'" :rows="5" type="textarea" /> </el-form-item> <el-input placeholder="请输入内容" v-model="input3" class="input-with-select"> <el-select v-model="select" slot="prepend" placeholder="请选择"> <el-option label="餐厅名" value="1"></el-option> <el-option label="订单号" value="2"></el-option> <el-option label="用户电话" value="3"></el-option> </el-select> <el-button slot="append" icon="el-icon-search"></el-button> </el-input> <el-form-item> <el-button type="primary" @click="handleSubmit">提交</el-button> <el-button @click="handleReset">重置</el-button> </el-form-item> </el-form> </template> <script> import RadioGroup from "./CustomRadio.vue"; import customCheckbox from "./customCheckbox.vue"; import pageOne from "./pageOne.vue"; import customInput from "./customInput.vue"; export default { components: { RadioGroup, customCheckbox, pageOne,customInput}, data() { return { // 表单数据(与 prop 对应) formData: { gender: null, // 性别(初始值为 null,触发必填校验) education: "", hobbies: [], inputmsg:'' }, // 选项列表 genderOptions: [ { label: "男", value: "male" }, { label: "女", value: "female" }, ], educationOptions: [ { label: "本科", value: "bachelor" }, { label: "硕士", value: "master" }, { label: "博士", value: "doctor" }, ], checkboxlistopts: [ { label: "选项1", value: "1" }, { label: "选项2", value: "2" }, { label: "选项3", value: "3" }, ], checkboxRules: [ // { required: true, message: "请选择checkbox", trigger: "change" }, { type: "array", required: true, message: "请至少选择", trigger: "change", }, { validator: (rule, value, callback) => { if (value && value.length > 2) { callback(new Error("最多两个")); } else { callback(); } }, trigger: "change", }, ], // 校验规则(与 Element 规则完全一致) genderRules: [ { required: true, message: "请选择性别", trigger: "change" }, ], inputmsgRules:[ { required: true, message: "不为空", trigger: "blur" }, ], educationRules: [ { required: true, message: "请选择学历", trigger: "change" }, // 自定义校验示例:禁止选择“本科”(仅作演示) { validator: (rule, value, callback) => { if (value === "bachelor") { callback(new Error("本科学历不符合要求")); } else { callback(); // 校验通过 } }, trigger: "change", }, ], }; }, methods: { // 提交表单:全量校验 handleSubmit() { this.$refs.formRef.validate((valid) => { if (valid) { console.log("表单校验通过,提交数据:", this.formData); // 调用接口提交... } else { console.log("表单校验失败"); return false; } }); }, // 重置表单 handleReset() { this.$refs.formRef.resetFields(); }, }, }; </script> <style scoped> .el-select, .el-input { width: 130px; } .input-with-select .el-input-group__prepend { background-color: #fff; } </style>
子组件 CustomRadio.vue
<template> <!-- 利用 el-form-item 承接校验规则和错误提示 --> <el-radio-group v-model="innerValue" :disabled="disabled" :size="size" @change="handleChange" > <!-- 遍历选项生成单选按钮 --> <el-radio v-for="(option, index) in options" :key="option.value || index" :label="option.value" :disabled="option.disabled || disabled" :border="border" > <!-- 支持默认文本或自定义插槽 --> <slot :option="option"> {{ option.label }} </slot> </el-radio> </el-radio-group> </template> <script> export default { name: "RadioGroup", props: { // 3. v-model 绑定值(父组件表单数据) value: { type: [String, Number, Boolean], default: null, }, // 4. 选项列表(格式:[{ label: '显示文本', value: '值', disabled: false }]) options: { type: Array, required: true, validator: (val) => { // 校验选项必须包含 label 和 value return val.every((item) => "label" in item && "value" in item); }, }, disabled: { type: Boolean, default: false, }, border: { type: Boolean, default: false, }, // 手动控制错误信息(可选,优先级高于校验规则的错误) errorMsg: { type: String, default: "", }, }, data() { return { // 内部值,避免直接修改 props // innerValue: this.value, }; }, // watch: { // // 同步父组件传入的 value 到内部 // value(newVal) { // this.innerValue = newVal; // }, // // 内部值变化时同步到父组件(v-model) // innerValue(newVal) { // this.$emit('input', newVal); // }, // }, computed: { size() { // 实际根据系统获取值 store return "medium"; }, innerValue: { get() { return this.value; }, set(val) { this.$emit("input", val); }, }, }, methods: { // 处理选择变化,触发校验通知 handleChange(val) { this.$emit("input", val); }, }, }; </script>
子组件 customCheckbox.vue
<template>
<div>
<!-- 复选框组 -->
<el-checkbox-group
v-model="checkedValues"
:disabled="disabled"
@change="handleChange"
:size="size"
>
<el-checkbox
v-for="(option, index) in options"
:key="option.value || index"
:label="option.value"
:disabled="option.disabled || disabled"
>
<template v-if="option.slot">
<slot :name="option.slot" :option="option"></slot>
</template>
<template v-else>
{{ option.label }}
</template>
</el-checkbox>
</el-checkbox-group>
</div>
</template>
<script>
export default {
name: "CustomCheckbox",
props: {
value: {
type: Array,
default: () => [],
},
options: {
type: Array,
required: true,
default: () => [],
},
min:{
type: Number,
default: null
},
max:{
type: Number,
default: null
},
disabled: {
type: Boolean,
default: false,
},
errorMsg: {
type: String,
default: "",
},
},
computed: {
size() {
return "medium"; // 可根据实际需求从全局获取
},
checkedValues: {
get() {
return Array.isArray(this.value) ? [...this.value] : [];
},
set(val) {
const uniqueVal = [...new Set(val)]; // 去重
this.$emit("input", uniqueVal);
this.$emit("change", uniqueVal);
},
},
},
methods: {
handleChange(val) {
this.$emit("change", val);
// 通知 el-form-item 触发验证
this.$parent.$emit("el.form.change", val);
},
},
};
</script>
<style scoped>
.all-checkbox {
margin-bottom: 8px;
}
.checkbox-list {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
</style>
子组件customInput.vue
<template> <div class="custom-input-wrapper"> <el-input :id="inputId" v-model="currentValue" :placeholder="placeholder || `请输入${label || ''}`" :type="type" :disabled="disabled" :size="size" :maxlength="maxlength" :show-word-limit="showWordLimit" :clearable="clearable" :prefix-icon="prefixIcon" :suffix-icon="suffixIcon" :readonly="readonly" :autocomplete="autocomplete" :validate-status="validateStatus" :style="inputStyle" :rows="rows" :autosize="autosize" @input="handleInput" @change="handleChange" @focus="handleFocus" @blur="handleBlur" @clear="handleClear" /> <!-- 错误提示(可选) --> <div v-if="errorMessage && validateStatus === 'error'" class="error-message" > {{ errorMessage }} </div> </div> </template> <script> export default { name: "CustomInput", props: { // 绑定值(v-model) value: { type: [String, Number], default: "", }, // 输入框ID(用于label关联) inputId: { type: String, default: () => `custom-input-${Date.now()}`, }, rows: { type: Number, default: null, }, autosize: { type: Object, default: null, }, // 标签文本 label: { type: String, default: "", }, // 是否必填 required: { type: Boolean, default: false, }, // 输入框类型(text/password/number等) type: { type: String, default: "text", }, // 占位符 placeholder: { type: String, default: "", }, // 是否禁用 disabled: { type: Boolean, default: false, }, // 尺寸(medium/small/mini) size: { type: String, default: "medium", validator: (val) => ["medium", "small", "mini"].includes(val), }, // 最大长度 maxlength: { type: Number, default: null, }, // 是否显示字数统计 showWordLimit: { type: Boolean, default: false, }, // 是否显示清空按钮 clearable: { type: Boolean, default: false, }, // 前缀图标 prefixIcon: { type: String, default: "", }, // 后缀图标 suffixIcon: { type: String, default: "", }, // 是否只读 readonly: { type: Boolean, default: false, }, // 自动完成(on/off) autocomplete: { type: String, default: "off", }, // 验证状态(success/error/validating) validateStatus: { type: String, default: "", }, // 错误提示信息 errorMessage: { type: String, default: "", }, // 自定义输入框样式 inputStyle: { type: Object, default: () => ({}), }, }, data() { return { // 内部维护的绑定值 // currentValue: this.value }; }, computed: { currentValue: { get() { // 监听外部值变化,同步到内部 return this.value; }, set(val) { // 监听内部值变化,同步到外部 this.$emit("input", val); }, }, }, methods: { // 输入事件(实时触发) handleInput(val) { this.$emit("input", val); // 确保v-model同步 this.$emit("input-change", val); // 自定义事件,区分原生input }, // 改变事件(失焦时值变化触发) handleChange(val) { this.$emit("change", val); }, // 聚焦事件 handleFocus(e) { this.$emit("focus", e); }, // 失焦事件 handleBlur(e) { this.$emit("blur", e); }, // 清空事件 handleClear() { this.$emit("clear"); }, }, }; </script> <style scoped> .custom-input-wrapper { margin-bottom: 16px; } .custom-input-label { display: inline-block; margin-bottom: 8px; font-size: 14px; color: #606266; } .required-mark { color: #f56c6c; margin-left: 4px; } .error-message { margin-top: 4px; font-size: 12px; color: #f56c6c; } </style>
浙公网安备 33010602011771号