heyujie
2021-06-07 e7d93ffbf1afeaf167af36ea4835935be967f900
components/IPInput.vue
@@ -1,229 +1,223 @@
<template>
  <div class="ip-input-container">
    <div class="ip-segment" v-for="(segment, index) in segments" :key="index">
      <input
        type="text"
        maxlength="3"
        class="ip-segment-input"
        :value="segment"
        :placeholder="placeholder"
        :disabled="disabled"
        v-on:keydown="onInputKeydown($event, index)"
        v-on:input="onInput($event, index)"
        v-on:blur="onInputBlur()"
        v-on:paste="onPaste($event, index)"
      />
      <i v-show="index != segments.length - 1">.</i>
    </div>
  </div>
</template>
<script>
function getRange(el) {
  var cuRange;
  var tbRange;
  var headRange;
  var range;
  var dupRange;
  var ret = {};
  if (el.setSelectionRange) {
    // standard
    ret.begin = el.selectionStart;
    ret.end = el.selectionEnd;
    ret.result = el.value.substring(ret.begin, ret.end);
  } else if (document.selection) {
    // ie
    if (el.tagName.toLowerCase() === "input") {
      cuRange = document.selection.createRange();
      tbRange = el.createTextRange();
      tbRange.collapse(true);
      tbRange.select();
      headRange = document.selection.createRange();
      headRange.setEndPoint("EndToEnd", cuRange);
      ret.begin = headRange.text.length - cuRange.text.length;
      ret.end = headRange.text.length;
      ret.result = cuRange.text;
      cuRange.select();
    } else if (el.tagName.toLowerCase() === "textarea") {
      range = document.selection.createRange();
      dupRange = range.duplicate();
      dupRange.moveToElementText(el);
      dupRange.setEndPoint("EndToEnd", range);
      ret.begin = dupRange.text.length - range.text.length;
      ret.end = dupRange.text.length;
      ret.result = range.text;
    }
  }
  el.focus();
  return ret;
}
export default {
  props: {
    ip: {
      type: String,
      defalut: ""
    },
    item: {},
    placeholder: String,
    onChange: Function,
    onBlur: Function,
    onItemChange: Function,
    disabled: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      segments: ["", "", "", ""]
    };
  },
  watch: {
    ip(ip) {
      this.syncIp(ip);
    }
  },
  methods: {
    onInputKeydown(event, index) {
      var keyCode = event.keyCode || event.which;
      var value = event.target.value;
      if (keyCode === 8 || keyCode === 37) {
        // move the cursor to previous input if backspace and left arrow is pressed at the begin of one input
        if (
          (value.length === 0 || getRange(event.target).end === 0) &&
          index > 0
        ) {
          this.$el.getElementsByTagName("input")[index - 1].focus();
          // When jump to pre input(enter "backspace"), thr cursor should in the end.
          // before fix: 127.|0.0.0  =>   12|7.0.0.1
          // after fix: 127.|0.0.0 = >   127|.0.0.0
          // notes: "|" mean the cursor position.
          event.preventDefault();
        }
      } else if (keyCode === 39 && keyCode === 110) {
        if (getRange(event.target).end === value.length && index < 3) {
          // move to cursor to the next input if right arrow is pressed at the end of one input
          this.$el.getElementsByTagName("input")[index + 1].focus();
        }
      }
    },
    onInput(event, index) {
      var value = event.target.value;
      event.target.value = this.segments[index];
      var segment = Number(value);
      if (isNaN(segment)) {
        return;
      } else if (value === "") {
        this.segments.splice(index, 1, "");
      } else if (segment > 255 || segment < 0) {
        // set the segment to 255 if out of ip range
        this.segments.splice(index, 1, 255);
      } else {
        this.segments.splice(index, 1, segment);
      }
      // jump to next input
      if (
        (value.length === 3 && index < 3) ||
        value[value.length - 1] === "."
      ) {
        this.$el.getElementsByTagName("input")[index + 1].focus();
      }
    },
    onInputBlur() {
      setTimeout(() => {
        this.$emit("on-blur", this.segments.join("."));
        if (this.onBlur) {
          this.onBlur(this.segments.join("."));
        }
        if (this.onItemChange) {
          this.onItemChange(this.segments.join("."), this.item)
        }
      }, 50);
    },
    onPaste(e, index) {
      var pasteText = e.clipboardData.getData("text/plain");
      var segments = pasteText.split(".");
      segments.forEach((segment, i) => {
        if (
          index + i < 4 &&
          !isNaN(segment) &&
          segment >= 0 &&
          segment <= 255
        ) {
          this.segments.splice(index + i, 1, segment);
        }
      });
      e.preventDefault();
    },
    syncIp(ip) {
      if (ip && ip.indexOf(".") !== -1) {
        ip.split(".").map((segment, index) => {
          if (isNaN(segment) || segment < 0 || segment > 255) {
            segment = 255;
          }
          this.segments.splice(index, 1, segment);
          return segment;
        });
      }
    }
  },
  mounted() {
    this.syncIp(this.ip);
    this.$watch(
      () => {
        return this.segments.join(".");
      },
      (val, oldValue) => {
        if (val !== oldValue) {
          if (val === "...") {
            val = "";
          }
          if (this.onChange) {
            this.onChange(val);
          }
        }
      }
    );
  }
};
</script>
<style lang="scss" scoped>
.ip-input-container {
     display: inline-block;
     width: 300px;
     height: 32px;
     line-height: normal;
     border: 1px solid #dcdfe6;
     box-sizing: border-box;
     background-color: #fff;
     text-align: left;
     /* max-width: 360px; */
     display: flex;
}
.ip-segment {
     width: 25%;
     height: 32px;
     line-height: normal;
     display: flex;
     justify-content: left;
  input {
  width: auto;
      height: 32px;
      line-height: normal;
      border: none;
      outline: none;
      text-align: center;
      text-indent: 0px;
      margin: 0px;
      padding: 0px;
      background-color: aliceblue;
    background-color: transparent;
  }
  i {
       display: inline-block;
       font-size: 20px;
       line-height: 35px;
  }
}
</style>
<template>
   <div class="ip-input-container">
      <div class="ip-segment" v-for="(segment, index) in segments" :key="index">
         <input type="text" maxlength="3" class="ip-segment-input" :value="segment" :placeholder="placeholder"
            :disabled="disabled" v-on:keydown="onInputKeydown($event, index)" v-on:input="onInput($event, index)"
            v-on:blur="onInputBlur()" v-on:paste="onPaste($event, index)" />
         <i v-show="index != segments.length - 1">.</i>
      </div>
   </div>
</template>
<script>
   function getRange(el) {
      var cuRange;
      var tbRange;
      var headRange;
      var range;
      var dupRange;
      var ret = {};
      if (el.setSelectionRange) {
         // standard
         ret.begin = el.selectionStart;
         ret.end = el.selectionEnd;
         ret.result = el.value.substring(ret.begin, ret.end);
      } else if (document.selection) {
         // ie
         if (el.tagName.toLowerCase() === "input") {
            cuRange = document.selection.createRange();
            tbRange = el.createTextRange();
            tbRange.collapse(true);
            tbRange.select();
            headRange = document.selection.createRange();
            headRange.setEndPoint("EndToEnd", cuRange);
            ret.begin = headRange.text.length - cuRange.text.length;
            ret.end = headRange.text.length;
            ret.result = cuRange.text;
            cuRange.select();
         } else if (el.tagName.toLowerCase() === "textarea") {
            range = document.selection.createRange();
            dupRange = range.duplicate();
            dupRange.moveToElementText(el);
            dupRange.setEndPoint("EndToEnd", range);
            ret.begin = dupRange.text.length - range.text.length;
            ret.end = dupRange.text.length;
            ret.result = range.text;
         }
      }
      el.focus();
      return ret;
   }
   export default {
      props: {
         ip: {
            type: String,
            defalut: ""
         },
         item: {},
         placeholder: String,
         onChange: Function,
         onBlur: Function,
         onItemChange: Function,
         disabled: {
            type: Boolean,
            default: false
         }
      },
      data() {
         return {
            segments: ["", "", "", ""]
         };
      },
      watch: {
         ip(ip) {
            this.syncIp(ip);
         }
      },
      methods: {
         onInputKeydown(event, index) {
            var keyCode = event.keyCode || event.which;
            var value = event.target.value;
            if (keyCode === 8 || keyCode === 37) {
               // move the cursor to previous input if backspace and left arrow is pressed at the begin of one input
               if (
                  (value.length === 0 || getRange(event.target).end === 0) &&
                  index > 0
               ) {
                  this.$el.getElementsByTagName("input")[index - 1].focus();
                  // When jump to pre input(enter "backspace"), thr cursor should in the end.
                  // before fix: 127.|0.0.0  =>   12|7.0.0.1
                  // after fix: 127.|0.0.0 = >   127|.0.0.0
                  // notes: "|" mean the cursor position.
                  event.preventDefault();
               }
            } else if (keyCode === 39 && keyCode === 110) {
               if (getRange(event.target).end === value.length && index < 3) {
                  // move to cursor to the next input if right arrow is pressed at the end of one input
                  this.$el.getElementsByTagName("input")[index + 1].focus();
               }
            }
         },
         onInput(event, index) {
            var value = event.target.value;
            event.target.value = this.segments[index];
            var segment = Number(value);
            if (isNaN(segment)) {
               return;
            } else if (value === "") {
               this.segments.splice(index, 1, "");
            } else if (segment > 255 || segment < 0) {
               // set the segment to 255 if out of ip range
               this.segments.splice(index, 1, 255);
            } else {
               this.segments.splice(index, 1, segment);
            }
            // jump to next input
            if (
               (value.length === 3 && index < 3) ||
               value[value.length - 1] === "."
            ) {
               this.$el.getElementsByTagName("input")[index + 1].focus();
            }
         },
         onInputBlur() {
            setTimeout(() => {
               this.$emit("on-blur", this.segments.join("."));
               if (this.onBlur) {
                  this.onBlur(this.segments.join("."));
               }
               if (this.onItemChange) {
                  this.onItemChange(this.segments.join("."), this.item)
               }
            }, 50);
         },
         onPaste(e, index) {
            var pasteText = e.clipboardData.getData("text/plain");
            var segments = pasteText.split(".");
            segments.forEach((segment, i) => {
               if (
                  index + i < 4 &&
                  !isNaN(segment) &&
                  segment >= 0 &&
                  segment <= 255
               ) {
                  this.segments.splice(index + i, 1, segment);
               }
            });
            e.preventDefault();
         },
         syncIp(ip) {
            if (ip && ip.indexOf(".") !== -1) {
               ip.split(".").map((segment, index) => {
                  if (isNaN(segment) || segment < 0 || segment > 255) {
                     segment = 255;
                  }
                  this.segments.splice(index, 1, segment);
                  return segment;
               });
            }
         }
      },
      mounted() {
         this.syncIp(this.ip);
         this.$watch(
            () => {
               return this.segments.join(".");
            },
            (val, oldValue) => {
               if (val !== oldValue) {
                  if (val === "...") {
                     val = "";
                  }
                  if (this.onChange) {
                     this.onChange(val);
                  }
               }
            }
         );
      }
   };
</script>
<style lang="scss" scoped>
   .ip-input-container {
      display: inline-block;
      width: 300px;
      height: 32px;
      line-height: normal;
      border: 1px solid #dcdfe6;
      box-sizing: border-box;
      background-color: #fff;
      text-align: left;
      /* max-width: 360px; */
      display: flex;
   }
   .ip-segment {
      width: 25%;
      height: 32px;
      line-height: normal;
      display: flex;
      justify-content: left;
      input {
         width: auto;
         height: 32px;
         line-height: normal;
         border: none;
         outline: none;
         text-align: center;
         text-indent: 0px;
         margin: 0px;
         padding: 0px;
         background-color: aliceblue;
         background-color: transparent;
      }
      i {
         display: inline-block;
         font-size: 20px;
         line-height: 35px;
      }
   }
</style>