目录

1.双向绑定

2.:oninput动态绑定和@input事件处理

3.主要功能

4.自定义组件代码


1.双向绑定

实现双向绑定在vue2.x版本中是使用v-model实现的,本文子类中的主要代码如下:

export default {
  name: 'IpInput',
  model: {
    prop: 'ipAddress',
    event: 'change'
  },
  props: {
    ipAddress: {
      type: String,
      required: true
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    // 失去焦点时判断输入是否完成,向父组件传递ip
    submitIp() {
      this.$nextTick(() => {
        if (!(this.ip1 === '') && !(this.ip2 === '') && !(this.ip3 === '') && !(this.ip4 === '')) {
          const ipVal = this.ip1 + '.' + this.ip2 + '.' + this.ip3 + '.' + this.ip4
          console.log(ipVal)
          this.$emit('change', ipVal)
        } else {
          return
        }
      })
    }
  }
}

父类中的调用如下

<ip-input v-model="tab.server" :disabled="tab.format !=='RTMP Push' && tab.format !=='RTMPS Push'" />

这里的tab.server值将会传给这个名为ipAddress的prop,同时当<ip-input>触发一个 change事件并附带一个新的值的时候,这个tab.server的property将会被更新。在submitIp()这个方法中调用this.$emit('change', ipVal)方法将新的值返回给父组件。

2.:oninput动态绑定和@input事件处理

1)使用oninput绑定,此方法会导致中文输入法情况下v-model和value不一致,原因是在中文输入法情况下vue中的v-model会自动return,value可变,但是v-mode绑定的值不变了。

oninput="value=value.replace(/[^0-9]/g, '')"

2)使用:oninput动态绑定,此方法必须在watch中搭配this.$nextTick(() => {})才能使dom数据正确更新,因为watch中使用了this.$refs.ip1.focus()来操作dom,vue中的dom是异步更新的,this.$nextTick(() => {})是一个微任务,在我看来类似Promise,ES6中Promise非常重要,像this.$store.dispatch(),axios等等都是Promise,主要是用来使异步任务通过.then()顺序执行,js的异步执行是先执行执行栈中的同步任务,执行栈为空后从任务队列中取数据,这里我也只是笼统的提一下,我自己也只是自学了一个月前端,还在学习状态,只了解点皮毛。

:oninput="ip1=ip1.replace(/[^0-9]/g, '')"

3)使用@input事件处理

@input="ip1=ip1.replace(/[^0-9]/g, '')"

这里@input和:oninput的区别在输出上主要是:

:oninput会在输入中文和英文的时候执行watch方法,说明这种情况下输入中文或英文会监听到输入框中ip的改变,且经我测试会执行两次。

@input情况下输入中文或英文不会执行watch方法,说明这种情况下输入中文或英文会直接改变值。

原因我猜测是:oninput是通过v-bind进行值绑定而@是v-on绑定的是事件,会直接执行里面的语句,希望懂的朋友在评论区指导一下。

3.主要功能

1)无法输入非数字,输入法情况下在输入完成后进行处理

2)输入三位数字后焦点自动向右跳转

3)按"↑"键焦点左移,按"↓"键焦点右移(这里暂时没有解决按左右键同时可以在输入框内部和输入框之间进行光标跳转,懂的朋友可以在评论区指导一下)

4)非0自动去除前面的0,例:036->36,000->0

5)输入框为空自动置0

element好看的输入框_javascript

4.自定义组件代码

自定义组件代码如下:

<template>
  <div :class="disabled ? 'disabled' : ''" class="ip-box">
    <el-input
      ref="ip1"
      :disabled="disabled"
      v-model="ip1"
      maxlength="3"
      @input="ip1=ip1.replace(/[^\d]/g,'')"
      @keyup.native="keyupEvent(1,$event)"
      @blur="syncIp1(),submitIp()"/>
    <div class="ip-dot" />
    <el-input
      ref="ip2"
      :disabled="disabled"
      v-model="ip2"
      maxlength="3"
      @input="ip2=ip2.replace(/[^\d]/g,'')"
      @keyup.native="keyupEvent(2,$event)"
      @blur="syncIp2(),submitIp()"/>
    <div class="ip-dot" />
    <el-input
      ref="ip3"
      :disabled="disabled"
      v-model="ip3"
      maxlength="3"
      @input="ip3=ip3.replace(/[^\d]/g,'')"
      @keyup.native="keyupEvent(3,$event)"
      @blur="syncIp3(),submitIp()"/>
    <div class="ip-dot" />
    <el-input
      ref="ip4"
      :disabled="disabled"
      v-model="ip4"
      maxlength="3"
      @input="ip4=ip4.replace(/[^\d]/g,'')"
      @keyup.native="keyupEvent(4,$event)"
      @blur="syncIp4(),submitIp()"/>
  </div>
</template>

<script>
export default {
  name: 'IpInput',
  model: {
    prop: 'ipAddress',
    event: 'change'
  },
  props: {
    ipAddress: {
      type: String,
      required: true
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      ip1: '',
      ip2: '',
      ip3: '',
      ip4: ''
    }
  },
  watch: {
    'ip1': {
      handler: function() {
        this.$nextTick(() => {
          console.log(this.ip1)
          if (this.ip1.length === 3) {
            this.$refs.ip2.focus()
          }
        })
      }
    },
    'ip2': {
      handler: function() {
        this.$nextTick(() => {
          if (this.ip2.length === 3) {
            this.$refs.ip3.focus()
          }
        })
      }
    },
    'ip3': {
      handler: function() {
        this.$nextTick(() => {
          if (this.ip3.length === 3) {
            this.$refs.ip4.focus()
          }
        })
      }
    }
  },
  created() {
    if (this.ipAddress !== '') {
      // split()将字符串分割为子字符串数组
      const ipList = this.ipAddress.split('.')
      this.ip1 = ipList[0]
      this.ip2 = ipList[1]
      this.ip3 = ipList[2]
      this.ip4 = ipList[3]
    }
  },
  methods: {
    // 三位数自动跳转,ip超过255设置为255,去掉前面的0
    syncIp1() {
      if (this.ip1 === '') {
        this.ip1 = '0'
      } else if (parseInt(this.ip1) > 255) {
        this.ip1 = '255'
        this.$message({
          message: '这不是有效值,请指定一个0-255之间的值',
          type: 'error'
        })
      } else if (this.ip1 === '0' || this.ip1 === '00' || this.ip1 === '000') {
        this.ip1 = '0'
      } else {
        this.ip1 = this.ip1.replace(/\b(0+)/g, '')
      }
    },
    syncIp2() {
      if (this.ip2 === '') {
        this.ip2 = '0'
      } else if (parseInt(this.ip2) > 255) {
        this.ip2 = '255'
        this.$message({
          message: '这不是有效值,请指定一个0-255之间的值',
          type: 'error'
        })
      } else if (this.ip2 === '0' || this.ip2 === '00' || this.ip2 === '000') {
        this.ip2 = '0'
      } else {
        this.ip2 = this.ip2.replace(/\b(0+)/g, '')
      }
    },
    syncIp3() {
      if (this.ip3 === '') {
        this.ip3 = '0'
      } else if (parseInt(this.ip3) > 255) {
        this.ip3 = '255'
        this.$message({
          message: '这不是有效值,请指定一个0-255之间的值',
          type: 'error'
        })
      } else if (this.ip3 === '0' || this.ip3 === '00' || this.ip3 === '000') {
        this.ip3 = '0'
      } else {
        this.ip3 = this.ip3.replace(/\b(0+)/g, '')
      }
    },
    syncIp4() {
      if (this.ip4 === '') {
        this.ip4 = '0'
      } else if (parseInt(this.ip4) > 255) {
        this.ip4 = '255'
        this.$message({
          message: '这不是有效值,请指定一个0-255之间的值',
          type: 'error'
        })
      } else if (this.ip4 === '0' || this.ip4 === '00' || this.ip4 === '000') {
        this.ip4 = '0'
      } else {
        this.ip4 = this.ip4.replace(/\b(0+)/g, '')
      }
    },
    // 失去焦点时判断输入是否完成,向父组件传递ip
    submitIp() {
      if (!(this.ip1 === '') && !(this.ip2 === '') && !(this.ip3 === '') && !(this.ip4 === '')) {
        const ipVal = this.ip1 + '.' + this.ip2 + '.' + this.ip3 + '.' + this.ip4
        console.log(ipVal)
        this.$emit('change', ipVal)
      } else {
        return
      }
    },
    // 按下左键和右键焦点左右移动
    keyupEvent(index, e) {
      this.$nextTick(() => {
        // 按下'↑'键焦点左移
        if (e.keyCode === 38) {
          if (index === 2) {
            this.$refs.ip1.focus()
          } else if (index === 3) {
            this.$refs.ip2.focus()
          } else if (index === 4) {
            this.$refs.ip3.focus()
          }
        } else if (e.keyCode === 40) {
          // 按下'↓'键焦点右移
          if (index === 1) {
            this.$refs.ip2.focus()
          } else if (index === 2) {
            this.$refs.ip3.focus()
          } else if (index === 3) {
            this.$refs.ip4.focus()
          }
        }
      })
    }
  }
}
</script>

<style lang='scss' scoped>
.ip-box {
  width: 202px;
  border:1px solid #dcdfe6;
  border-radius: 5px;
  height: 38px; 
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  >>> .el-input__inner {
	border: 0 !important;
    padding: 0;
    text-align: center;
  }
  >>> .el-input {
    width: 48px;
  }
  &.disabled {
    background-color: #f5f7fa;
    color: #c0c4cc;
    cursor: not-allowed;
  }
}
.ip-dot {
  margin-top: 7px;
	display: inline-block;
	width: 3px;
	height: 3px;
	border-radius: 50%;
	background-color: #606266;
}
.disabled {
  .ip-dot {
    background-color: #c0c4cc;
  }
}
</style>

其中使用了scss,需要使用sass-loader,选择合适项目的版本

npm install sass-loader@版本号 --save-dev

element-ui中若要改变样式需要使用>>>深度选择器,或者/deep/深度选择器

父组件中调用如下:

<ip-input v-model="tab.server" :disabled="tab.format !=='RTMP Push' && tab.format !=='RTMPS Push'" />

其中的v-model绑定的是ip地址,String类型,disabled是否禁用输入(只读),默认是false

若是绑定ipAddress是从后端读取的,由于源代码是将ipAddress的读取放在create()方法中的,因此会立即读取没有延迟,可修改如下:

watch: {
    'ipAddress': {
      handler(newName, oldName) {
        if (newName !== '') {
          // split()将字符串分割为子字符串数组
          const ipList = newName.split('.')
          this.ip1 = ipList[0]
          this.ip2 = ipList[1]
          this.ip3 = ipList[2]
          this.ip4 = ipList[3]
        }
      },
      immediate: true
    }
  }

博主只是自学前端一个月,这段代码肯定有可以优化的地方,可以写的更精简,有什么问题可以评论讨论