惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
A
About on SuperTechFans
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 【当耐特】
W
WeLiveSecurity
博客园 - 三生石上(FineUI控件)
The Cloudflare Blog
I
InfoQ
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Application and Cybersecurity Blog
Application and Cybersecurity Blog
雷峰网
雷峰网
Hacker News - Newest:
Hacker News - Newest: "LLM"
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Troy Hunt's Blog
S
SegmentFault 最新的问题
Help Net Security
Help Net Security
博客园_首页
博客园 - 叶小钗
O
OpenAI News
PCI Perspectives
PCI Perspectives
月光博客
月光博客
人人都是产品经理
人人都是产品经理
B
Blog RSS Feed
GbyAI
GbyAI
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The Last Watchdog
The Last Watchdog
C
CXSECURITY Database RSS Feed - CXSecurity.com
有赞技术团队
有赞技术团队
D
Darknet – Hacking Tools, Hacker News & Cyber Security
腾讯CDC
Hacker News: Ask HN
Hacker News: Ask HN
I
Intezer
Y
Y Combinator Blog
阮一峰的网络日志
阮一峰的网络日志
Spread Privacy
Spread Privacy
T
Tailwind CSS Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
量子位
Cyberwarzone
Cyberwarzone
The Hacker News
The Hacker News
N
News and Events Feed by Topic
P
Proofpoint News Feed
Scott Helme
Scott Helme
D
Docker
Know Your Adversary
Know Your Adversary
Recent Commits to openclaw:main
Recent Commits to openclaw:main
TaoSecurity Blog
TaoSecurity Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tor Project blog

博客园 - Mc525

antv3 x6 基本语法-流程图(二) element ui el-form 表单错误 滚动可视区域 vue2 element ui el-table大数据处理无分页解决方案-分片加载 vue2 地图热力图 -下钻省市县 三级及地图资源文件 vue2 组件封装 el-select vue2 组件封装 el-input vue2 组件封装 el-date-picker 日期 vue2 scss sass 基础安装包、安装依赖报错 !!! Mac Jenkins 环境部署 vue2 按钮权限(七) vue2 换肤(六) vue2 项目实例 国际化(五) vue2 项目实例 动态路由菜单(四) vue2 项目实例 Mock数据模拟(三) vue2 项目实例 Layout布局(二) vue2 项目实例 项目初始化(一) vue2 项目架构--api.js(七) vue2 项目架构--vue.config.js(七) vue2 项目架构--main.js(六)
vue2 封装组件使用 v-mode【el-radio,el-input】
Mc525 · 2025-10-27 · via 博客园 - Mc525

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>