当前位置: 首页 > news >正文

vue2 封装组件使用 v-mode【el-radio,el-input】

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-formref="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-checkboxv-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-limitplaceholder="请输入用户名"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-groupv-model="innerValue":disabled="disabled":size="size"@change="handleChange"><!-- 遍历选项生成单选按钮 --><el-radiov-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 和 valuereturn 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() {// 实际根据系统获取值 storereturn "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-groupv-model="checkedValues":disabled="disabled"@change="handleChange":size="size"><el-checkboxv-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"/><!-- 错误提示(可选) --><divv-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>