vue 软键盘组件封装

场景和需求

1 软键盘固定
2 多输入框共用一个组件,聚焦切换时操作对象自动切换
3 根据光标在输入框的位置进行相应的输入和删除操作
4 点击软键盘时保存输入框光标活跃
5 输入框和键盘在一个弹窗组件中,弹窗打开时对其中一个输入框默认聚焦
6 样式方面,按键宽度自适应,可以给特定按键特定样式。
7 场景下特殊需求,当不对任意输入框聚焦时,默认对其中某个输入框进行操作。
image

实现

光标这个没做到输入框保持活跃,和删除光标处内容。
这里是获取光标位置的函数,但是当点击按钮时输入框就会blur,就无法获取了。

//获取光标位置函数
getCursorPosition(id) {
  const elem = document.querySelector(id);
  var pp = 0;
  // IE
  if (document.selection) {
    elem.focus();
    var aa = document.selection.createRange();
    aa.moveStart('character', -elem.value.length);
    pp = aa.text.length;
  }
  // FF, Chrome
  else if (elem.selectionStart || elem.selectionStart == '0') {
    pp = elem.selectionStart;
  }
  return pp;
},

子组件

<template>
  <div class="keyboards">
    <div class="keyboards-row" v-for="(keyArr, index) in activeKey" :key="index">
      <button
        class="keyboards-key"
        v-for="item in keyArr"
        v-text="item"
        :key="item + index"
        :class="{
          'keyboards-key--active': item === activeButton,
          'keyboards-key--number': type === 'number',
          'keyboards-key--all': type !== 'number',
          'keyboards-key--star': item === '*',
          'keyboards-key--option': isOption(type, item)
        }"
        @click="clickKey(item)"
        @mousedown="changeActiveButton(item)"
      />
    </div>
  </div>
</template>

<script>
export default {
  name: 'SoftKeyboard',
  props: {
    type: {
      type: String,
      // number || uppercase || lowercase
      default: () => 'number'
    },
    inputText: {
      type: String,
      default: () => ''
    },
    inputDom: {
      type: String,
      default: () => ''
    }
  },
  data() {
    return {
      activeKey: [],
      isUppercase: '',
      activeButton: '', // 当前按压按钮
      // 数字键盘布局
      numberKey: [
        ['7', '8', '9'],
        ['4', '5', '6'],
        ['1', '2', '3'],
        ['0', '*', '删除', '清空']
      ],
      // 全键布局
      allBigKey: [
        ['!', '@', '#', '$', '%', '^', '&', '*', '-', '_'],
        ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
        ['切换', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],
        ['删除', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '.', '清空']
      ],
      allsmallKey: [
        ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
        ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
        ['切换', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'],
        ['删除', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '.', '清空']
      ]
    };
  },
  created() {
    this.changeKeyborad(this.type);
    if (this.type === 'uppercase') {
      this.isUppercase = true;
    }
    document.addEventListener('mouseup', this.mouseUp);
  },
  mounted() {},
  destroyed() {
    // 组件销毁时移除添加到document上的事件,避免影响到其他组件
    document.removeEventListener('mouseup', this.mouseUp);
  },
  methods: {
    changeKeyborad(type) {
      switch (type) {
        case 'number':
          this.activeKey = this.numberKey;
          break;
        case 'uppercase':
          this.activeKey = this.allBigKey;
          break;
        case 'lowercase':
          this.activeKey = this.allsmallKey;
          break;
        default:
          break;
      }
    },
    isOption(type, btn) {
      if (type === 'number') {
        return false;
      }
      if (btn === '切换' || btn === '删除' || btn === '清空') {
        return true;
      }
      return false;
    },
    clickKey(key) {
      let value = this.inputText;
      switch (key) {
        case '删除':
          value = value.length ? value.slice(0, -1) : value;
          break;
        case '清空':
          value = '';
          break;
        case '切换':
          this.isUppercase = !this.isUppercase;
          this.activeKey = this.isUppercase ? this.allBigKey : this.allsmallKey;
          break;
        default:
          value = value + key;
          break;
      }
      this.$emit('onChange', value);
    },
    changeActiveButton(button) {
      this.activeButton = button;
    },
    //获取光标位置函数
    getCursorPosition(id) {
      const elem = document.querySelector(id);
      var pp = 0;
      // IE
      if (document.selection) {
        elem.focus();
        var aa = document.selection.createRange();
        aa.moveStart('character', -elem.value.length);
        pp = aa.text.length;
      }
      // FF, Chrome
      else if (elem.selectionStart || elem.selectionStart == '0') {
        pp = elem.selectionStart;
      }
      return pp;
    },
    mouseUp() {
      this.activeButton = '';
    }
  }
};
</script>
<style scoped lang="scss">
.keyboards {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  background-color: #f0f0f0;
  padding: 3px;
  border-radius: 5px;
  .keyboards-row {
    width: 100%;
    display: flex;
    justify-content: space-around;
    .keyboards-key {
      width: 100%;
      height: 56px;
      margin: 2px;
      padding: 5px;
      flex-grow: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      border: 1px solid #dedede;
      // border-radius: 4px;
      border-bottom: 1px solid #b5b5b5;
      border-radius: 5px;
      -webkit-box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.3);
      box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.3);
      cursor: pointer;
      font-size: 1.5em;
      font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Noto Sans CJK SC,
        WenQuanYi Micro Hei, Arial, sans-serif;
      font-weight: 400;
      background-color: #fdfdfd;
      // &:hover {
      //   box-shadow: 0 0 4px 2px #ccd1e7;
      // }
      &.keyboards-key--active {
        background-color: #e5e5e5;
      }
      // &.keyboards-key--number {
      //   width: 32%;
      // }
      &.keyboards-key--all {
        width: 9.1%;
      }
      &.keyboards-key--star {
        font-size: 2em;
      }
      &.keyboards-key--option {
        width: 20%;
      }
    }
  }
}
</style>

父组件

<el-form ref="modelForm" :model="modelForm" :rules="modelRules" label-width="60px" size="medium">
  <el-form-item label="输入框1"  prop="input1">
    <el-input
      id="input-input1"
      size="small"
      clearable
      v-model.trim="modelForm.input1"
      @focus="setInput('input1')"
      style="width: 100%"
    ></el-input>
  </el-form-item>
  <el-form-item label="输入框2" prop="input2">
    <el-input
      id="input-input2"
      size="small"
      maxlength="3"
      ref="input2"
      clearable
      v-model.trim="modelForm.input2"
      @focus="setInput('input2')"
    ></el-input>
  </el-form-item>
</el-form>
<softKeyBoard @onChange="onChange" type="number" :inputText="input" :inputDom="inputDom" />

<script>
export default {
  computed: {
    input() {
      if (this.activeInput) {
        return this.modelForm[this.activeInput];
      }
      return '';
    }
  },
  data() {
    return {
      activeInput: '',
    };
  },
  methods: {
    setInput(prop) {
      this.activeInput = prop;
    },
    onChange(input) {
      if (this.activeInput === 'input1') {
        input = input.slice(0, 7);
      }
      if (this.activeInput === 'input2') {
        input = input.slice(0, 3);
      }
      this.$set(this.modelForm, this.activeInput, input);
    },
  }
}
</script>
posted @ 2023-03-28 18:51  初学者-xjr  阅读(561)  评论(0编辑  收藏  举报