vue 封装input组件

本文介绍了一个基于Vue的自定义表单组件实现方法,详细解释了如何创建可复用的表单字段组件,并展示了如何在父组件中使用这些字段来构建完整的表单,包括输入验证和提交处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

涉及到的知识点其实就是父子组件之间属性和方法的使用

第一步:在components目录下新建一个子组件(field.vue)
<template>
  <div
    class="cell-wrapper"
    :class="showVerify ? 'include-verify flex-base' : ''"
  >
    <div
      class="cell-item flex-base"
      :style="{backgroundColor: cellBgColor}"
    >
      <div
        class="left"
        :class="label !== undefined && label !== '' ? 'flex-label' : ''"
      >
        <label>{{ label }}</label>
        <i class="van-icon" :class="`van-icon-${leftIcon}`"></i>
      </div>
      <div class="right flex-base">
        <input
          v-if="type === undefined"
          ref="input"
          type="text"
          v-on:value="value"
          :placeholder="placeHolder"
          :style="{backgroundColor: cellBgColor === undefined ? inputBgColor : cellBgColor}"
          @input="updateValue($event.target.value)"
          @focus="onFocus"
          @blur="onBlur"
          @click="clickInput"
        >

        <select
          v-if="type === 'select'"
          @change="changeSelect($event.target.value)"
        >
          <option value="">请选择</option>
          <option
            v-for="(option, index) in options"
            :key="index"
            :value="option.value"
          >
            {{ option.label }}
          </option>
        </select>

        <i
          class="van-icon van-icon-clear"
          v-if="!success && showClear"
          @click="clearValue"
        />
        <i
          class="van-icon van-icon-passed"
          v-if="success"
        />
        <p class="error">{{ errorMsg }}</p>
      </div>
    </div>
    <div
      v-if="showVerify"
      class="getCode"
      @click="getVerifyCode"
      :class="showCodeBtn ? '' : 'disabled-get'"
    >
      {{ showCodeBtn ? '获取验证码' : `${authTime}s后重试` }}
    </div>
  </div>
</template>

<script>
  export default {
    name: 'formCell',
    props: {
      type: {},
      label: {},
      leftIcon: {},
      clearIcon: {}, // 清除图标
      successIcon: {}, // 校验成功时显示的图标
      placeHolder: {}, // input的placeholder
      errorMsg: {}, // 显示错误信息
      inputBgColor: { // input输入框的背景
        type: String,
        default: '#F5F5F5'
      },
      cellBgColor: {}, // 整行背景
      success: { // 校验成功
        type: Boolean,
        default: false
      },
      showVerify: {}, // 显示验证码
      showCodeBtn: {}, // 显示获取验证码按钮
      authTime: {},
      options: { // 下拉选项
        type: Array
      }
    },
    data () {
      return {
        showClear: false,
        inputRef: '',
        value: ''
      }
    },
    methods: {
      setValue (val) {
        this.$emit('input', val);
      },

      // 更新选择项
      changeSelect (val) {
        this.$emit('change', val, this.label);
      },

      // 更新input的value值
      updateValue (val) {
        if (val) {
          this.showClear = true;
        } else {
          this.showClear = false;
        }
        this.setValue(val);
      },

      // 点击input无延迟聚焦
      clickInput () {
        this.inputRef.focus();
      },

      // 聚焦事件
      onFocus () {
        this.$emit('watchFocus');
      },

      // 失焦事件
      onBlur () {
        this.$emit('watchBlur');
      },

      // 清空input的value值
      clearValue () {
        this.inputRef.value = '';
        this.inputRef.focus();
        this.updateValue('');
      },

      getVerifyCode () {
        this.$emit('getVerifyCode')
      }
    },
    created () {
      let _this = this;
      this.$nextTick(() => {
        _this.inputRef = this.$refs.input;
      })
    }
  };
</script>

<style scoped lang="scss" type="text/scss">
  .getCode{
    flex: 1;
    font-size: 30px;
    color: #FFF;
    text-align: center;
    background: $theme;
    padding: 25px 5px;
    margin-left: 25px;
    border-radius: 20px;
  }

  .disabled-get{
    background: #8d8d8d;
  }

  .cell{
    &-wrapper{
      position: relative;
      padding: 15px 20px 30px;
    }
    &-item{
      padding: 15px 20px;
      .flex-label{
        flex: .2;
        text-align: right;
      }
      .left{
        margin-right: 20px;
        .van-icon{
          font-size: 45px;
          color: $theme;
        }
      }
      .right{
        position: relative;
        flex: 1;
        input{
          width: 100%;
          height: 60px;
          text-indent: 15px;
        }
        select{
          appearance:none;
          -moz-appearance:none;
          -webkit-appearance:none;
          width: 100%;
          height: 80px;
          border: none;
          outline: none;
          color: #888;
          font-size: 30px;
          text-indent: 15px;
          background: #F5F5F5;
          &::after{
            content: '';
            width: 50px;
            height: 50px;
            background: #000;
            position: absolute;
            right: 0;
          }
        }

        ::-webkit-input-placeholder { /* WebKit browsers */
          color: #888;
          font-size: 28px;
        }
        :-moz-placeholder { /* Mozilla Firefox 4 to 18 */
          color: #888;
          font-size: 28px;
        }
        ::-moz-placeholder { /* Mozilla Firefox 19+ */
          color: #888;
          font-size: 28px;
        }
        :-ms-input-placeholder { /* Internet Explorer 10+ */
          color: #888;
          font-size: 28px;
        }

        .van-icon{
          position: absolute;
          top: 20px;
          right: 10px;
          &-clear{
            color: $theme;
          }
          &-passed{
            color: #1bbb00;
          }
        }

        .error{
          position: absolute;
          bottom: -50px;
          font-size: 25px;
          color: $theme;
          margin: 0;
        }
      }
    }
  }

  .include-verify {
    .cell-item{
      flex: 2;
    }
  }
</style>

第二步、父组件使用field组件
<template>
  <div class="verified">
    <new-layout centerColor="#FFF">
      <template slot="center">
        <div class="field-group">
          <form-field
            leftIcon="yonghu"
            placeHolder="请输入您的姓名"
            cellBgColor="#F5F5F5"
            v-model="formData.name.value"
            :errorMsg="formData.name.err"
            :success="formData.name.success"
            @watchFocus="watchInputFocus('name')"
            @watchBlur="watchInputBlur(formData.name.value, 'name')"
          />

          <form-field
            leftIcon="Id"
            placeHolder="请输入您的身份证号"
            cellBgColor="#F5F5F5"
            v-model="formData.idCard.value"
            :errorMsg="formData.idCard.err"
            :success="formData.idCard.success"
            @watchFocus="watchInputFocus('id')"
            @watchBlur="watchInputBlur(formData.idCard.value, 'id')"
          />

          <form-field
            leftIcon="shouji1"
            placeHolder="请输入您的手机号"
            cellBgColor="#F5F5F5"
            v-model="formData.phone.value"
            :errorMsg="formData.phone.err"
            :success="formData.phone.success"
            @watchFocus="watchInputFocus('phone')"
            @watchBlur="watchInputBlur(formData.phone.value, 'phone')"
          />

          <form-field
            leftIcon="queren"
            placeHolder="请输入手机验证码"
            cellBgColor="#F5F5F5"
            v-model="formData.verifyCode.value"
            :errorMsg="formData.verifyCode.err"
            :success="formData.verifyCode.success"
            :showVerify="showVerify"
            :showCodeBtn="showCodeBtn"
            :authTime="authTime"
            @watchFocus="watchInputFocus('verify')"
            @watchBlur="watchInputBlur(formData.verifyCode.value, 'verify')"
            @getVerifyCode="getVerifyCode"
          />

          <div
            class="commit"
            @click="commitData"
            :class="isPassAll ? '' : 'disabled'"
          >
            确认提交
          </div>
        </div>
      </template>
    </new-layout>
  </div>
</template>

<style lang="scss" scoped>
  .field-group{
    padding-top: 20px;
  }
  .commit{
    width: 70%;
    padding: 25px;
    margin: 70px auto 30px;
    border-radius: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    background: $theme;
    color: #FFF;
  }

  .disabled{
    background: #8d8d8d;
  }
</style>

<script>
  import infoCell from '@/components/addresses/infoCell';
  import { _debounce } from '../../../mixins/utils';

  export default {
    name: '',
    components: {
      infoCell
    },
    data() {
      return {
        showVerify: true,
        showCodeBtn: true,
        clearAble: true, // input清除控件
        authTime : 0,
        // 表单数据
        formData: {
          name: {
            value: '',
            err: '',
            success: false
          },
          idCard: {
            value: '',
            err: '',
            success: false
          },
          phone: {
            value: '',
            err: '',
            success: false
          },
          verifyCode: {
            value: '',
            err: '',
            success: false
          }
        }
      };
    },
    computed: {
      isPassAll () {
        if (
          this.formData.name.success && this.formData.idCard.success &&
          this.formData.phone.success && this.formData.verifyCode.success
        ) {
          return true
        } else {
          return false
        }
      }
    },
    created() {
    },
    methods: {
      // 重置错误信息
      setErrMsg (value) {
        return value ? true : false;
      },

      clearValue () {
      },

      // 监听input组件聚焦事件
      watchInputFocus (flag) {
        switch (flag) {
          case 'name':
            this.formData.name.success = false;
            break;
          case 'id':
            this.formData.idCard.success = false;
            break;
          case 'phone':
            this.formData.phone.success = false;
            break;
          case 'verify':
            this.formData.verifyCode.success = false;
            break;
          default:
            break;
        }
      },

      // 监听input组件失焦事件
      watchInputBlur (value, flag) {
        switch (flag) {
          case 'name':
            this.formData.name.err = this.setErrMsg(value) ? '' : '请输入姓名';
            this.formData.name.success = this.setErrMsg(value) ? true : false;
            break;
          case 'id':
            this.formData.idCard.err = '';
            let idEmpty = this.setErrMsg(value);
            let isId = this.regIdCard(value);
            if (idEmpty) {
              if (!isId) {
                this.formData.idCard.err = '身份证号码格式错误'
              }
            } else {
              this.formData.idCard.err = '身份证号不能为空'
            }
            this.formData.idCard.success = idEmpty && isId ? true : false;
            break;
          case 'phone':
            let phoneEmpty = this.setErrMsg(value);
            let isPhone = this.regPhoneNumber(value);
            this.formData.phone.err = '';
            if (phoneEmpty) {
              if (!isPhone) {
                this.formData.phone.err = '手机号格式错误';
              }
            } else {
              this.formData.phone.err = '手机号不能为空';
            }
            this.formData.phone.success = phoneEmpty && isPhone ? true : false;
            break;
          case 'verify':
            this.formData.verifyCode.err = '';
            this.formData.verifyCode.err = this.setErrMsg(value) ? '' : '请输入验证码';
            this.formData.verifyCode.success = this.setErrMsg(value) ? true : false;
            break;
          default:
            break;
        }
      },

      /**
       * 获取验证码
       * 60s倒计时
       */
      getVerifyCode () {
        if (this.showCodeBtn) {
          this.showCodeBtn = false;
          this.authTime = 59;
          let authTimer = setInterval(() => {
            this.authTime--;
            if (this.authTime <= 0) {
              this.showCodeBtn = true;
              clearInterval(authTimer);
            }
          }, 1000);
        }
      },
      // 提交
      commitData: _debounce(function() {
        if (this.isPassAll) {
          console.log('提交表单数据!');
        }
      }, 800),
    }
  };
</script>

<think>好的,我需要帮助用户在Vue 2中创建并封装一个自定义的Input组件。根据用户提供的引用内容,特别是引用[1]和引用[3],我应该先整理出步骤。首先,创建组件文件,比如Input.vue,然后定义props来接收父组件传递的数据,比如类型、占位符等。接着,需要在输入框失去焦点时触发校验,参考引用[2]中的@blur事件处理。然后,通过$emit方法将输入的值传递给父组件,确保双向绑定。接下来,全局注册组件,像引用[1]中那样在component.js里注册,这样可以在任何地方使用。最后,在父组件中使用这个自定义Input组件,并通过v-model绑定数据。可能还需要考虑样式和错误提示的处理。用户可能还会关心如何进行表单验证,是否需要使用计算属性或者watch来监听输入变化。需要确保每一步都符合Vue 2的语法,比如使用v-model的prop和event默认设置。另外,是否需要处理其他事件,比如@input或者@change,也要根据需求来决定。最后,生成相关问题,比如如何扩展组件功能或集成验证库,帮助用户进一步学习。</think>### 在 Vue 2 中实现自定义 Input 组件的分步指南 #### 1. **创建自定义 Input 组件** 在 `src/components/Input` 目录下新建 `Input.vue` 文件: ```vue <template> <div class="custom-input"> <input :type="type" :placeholder="placeholder" :value="value" @input="$emit('input', $event.target.value)" @blur="handleBlur" > <div v-if="error" class="error-message">{{ error }}</div> </div> </template> <script> export default { name: 'CustomInput', props: { value: { type: [String, Number], default: '' }, type: { type: String, default: 'text' }, placeholder: { type: String, default: '请输入内容' }, validator: { type: Function } // 支持自定义校验函数 }, data() { return { error: '' } }, methods: { handleBlur(e) { if (this.validator) { this.error = this.validator(e.target.value) || '' } this.$emit('blur', e) // 支持父组件监听 blur 事件 } } } </script> <style scoped> .custom-input { margin-bottom: 10px; } .error-message { color: red; font-size: 12px; } </style> ``` #### 2. **全局注册组件(可选)** 在 `src/util/component.js` 中添加全局注册(参考引用[1]): ```javascript import CustomInput from '@/components/Input/Input.vue' export default (Vue) => { Vue.component('CustomInput', CustomInput) } ``` #### 3. **在父组件中使用** 通过 `v-model` 实现双向绑定(参考引用[3]的输入类型): ```vue <template> <div> <CustomInput v-model="username" type="text" placeholder="用户名" :validator="validateUsername" /> <CustomInput v-model="password" type="password" placeholder="密码" /> </div> </template> <script> export default { data() { return { username: '', password: '' } }, methods: { validateUsername(val) { return val.length < 3 ? '用户名需至少3个字符' : '' } } } </script> ``` #### 关键技术点 1. **双向绑定**:通过 `v-model` 语法糖实现,底层对应 `value` prop 和 `input` 事件[^3] 2. **扩展性**:支持 10+ 种输入类型(`text`/`number`/`date` 等),通过 `type` prop 控制 3. **验证机制**: - 内置失焦验证(@blur 事件) - 支持通过 `validator` prop 注入自定义校验函数(参考引用[2]的失焦处理) 4. **错误反馈**:通过 `error-message` 元素实现验证错误提示
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值