• vue——虚拟键盘simplekeyboard的使用,支持中英文切换 前端


     
    simple-keyboard官网:https://hodgef.com/simple-keyboard/
    (多刷新几次)
     
    效果:
     
     
    我是将输入框+虚拟键盘抽取为组件进行调用的

    1.安装依赖

    安装simpleKeyboard依赖:
    npm install simple-keyboard --save
    安装simpleKeyboard输入法依赖:
    npm install simple-keyboard-layouts --save
     

    2.组件

    2.1.虚拟键盘组件simpleKeyboard.vue

    <template>
      <div :class="keyboardClass"></div>
    </template>
    
    <script>
    import Keyboard from 'simple-keyboard';
    import 'simple-keyboard/build/css/index.css';
    import layout from 'simple-keyboard-layouts/build/layouts/chinese'; // 中文输入法
    
    export default {
      name: 'SimpleKeyboard',
      props: {
        keyboardClass: {
          default: 'simple-keyboard',
          type: String,
        },
        input: {
          default: '',
        },
        maxLength: { default: '' },
      },
      data: () => ({
        keyboard: null,
        displayDefault: {
          '{bksp}': 'backspace',
          '{lock}': 'caps',
          '{enter}': '> enter',
          '{tab}': 'tab',
          '{shift}': 'shift',
          '{change}': '英文',
          '{space}': ' ',
          '{clear}': '清空',
          '{close}': '关闭',
        },
      }),
      mounted() {
        this.keyboard = new Keyboard(this.keyboardClass, {
          onChange: this.onChange,
          onKeyPress: this.onKeyPress,
          layoutCandidates: layout.layoutCandidates,
          layout: {
            // 默认布局
            default: [
              '` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
              '{tab} q w e r t y u i o p [ ] \\',
              "{lock} a s d f g h j k l ; ' {enter}",
              '{shift} z x c v b n m , . / {clear}',
              '{change} {space} {close}',
            ],
            // shift布局
            shift: [
              '~ ! @ # $ % ^ & * ( ) _ + {bksp}',
              '{tab} Q W E R T Y U I O P { } |',
              '{lock} A S D F G H J K L : " {enter}',
              '{shift} Z X C V B N M &lt; &gt; ? {clear}',
              '{change} {space} {close}',
            ],
          },
          // 按钮展示文字
          display: this.displayDefault,
          // 按钮样式
          buttonTheme: [
            {
              class: 'hg-red close',
              buttons: '{close}',
            },
            {
              class: 'change',
              buttons: '{change}',
            },
          ],
          // 输入限制长度
          maxLength: this.maxLength,
        });
      },
      methods: {
        onChange(input) {
          this.keyboard.setInput(input);
          this.$emit('onChange', input);
        },
        // 点击键盘
        onKeyPress(button, $event) {
          // 点击关闭
          if (button === '{close}') {
            let keyboard = $event.path[3];
            keyboard.style.visibility = 'hidden';
            return false;
          } else if (button === '{change}') {
            // 切换中英文输入法
            if (this.keyboard.options.layoutCandidates !== null) {
              this.$set(this.displayDefault, '{change}', '中文');
              // 切换至英文
              this.keyboard.setOptions({
                layoutCandidates: null,
                display: this.displayDefault,
              });
            } else {
              // 切换至中文
              this.$set(this.displayDefault, '{change}', '英文');
              this.keyboard.setOptions({
                layoutCandidates: layout.layoutCandidates,
                display: this.displayDefault,
              });
            }
          } else if (button === '{clear}') {
            this.keyboard.clearInput();
          } else {
            let value =
              $event.target.offsetParent.parentElement.children[0].children[0]
                .value;
            // 输入框有默认值时,覆写
            if (value) {
              this.keyboard.setInput(value);
            }
            this.$emit('onKeyPress', button);
          }
          if (button === '{shift}' || button === '{lock}') this.handleShift();
        },
        // 切换shift/默认布局
        handleShift() {
          let currentLayout = this.keyboard.options.layoutName;
          let shiftToggle = currentLayout === 'default' ? 'shift' : 'default';
    
          this.keyboard.setOptions({
            layoutName: shiftToggle,
          });
        },
      },
      watch: {
        input(input) {
          this.keyboard.setInput(input);
        },
      },
    };
    </script>
    
    <style lang="less">
    @deep: ~'>>>';
    .hg-theme-default {
       70%;
      .hg-button {
        &.hg-red {
          background: #db3e5d;
          color: white;
          &.close {
            max- 200px;
          }
        }
        &.change {
          max- 200px;
        }
      }
    }
    </style>

    2.2 输入框组件keyboard-input.vue

    <template>
      <div class="input-keyboard">
        <el-input
          v-model="inputValue"
          :autofocus="autofocus"
          :class="inputClass"
          :suffix-icon="suffixIcon"
          :type="type"
          :show-password="showPassword"
          :rows="rows"
          :show-word-limit="showWordLimit"
          :disabled="disabled"
          :maxlength="maxlength"
          :clearable="clearable"
          :size="size"
          :placeholder="placeholder"
          @focus="focusInput($event)"
          @input="inputFun"
        >
          <template v-if="appendPort" slot="append">[1-65535]</template></el-input
        ><SimpleKeyboard
          :ref="keyboardClass"
          :keyboardClass="keyboardClass"
          @onChange="onChange"
          @onKeyPress="onKeyPress"
          :input="inputValue"
          :maxLength="maxlength"
        />
      </div>
    </template>
    
    <script>
    import SimpleKeyboard from './simpleKeyboard.vue';
    
    export default {
      name: 'keyboard-input',
      components: {
        SimpleKeyboard,
      },
      props: {
        keyboardClass: String,
        autofocus: Boolean,
        field: String,
        value: {
          default: '',
        },
        inputClass: String,
        type: {
          type: String,
          default: 'text',
        },
        showPassword: {
          type: Boolean,
          default: false,
        },
        rows: Number,
        showWordLimit: {
          default: true,
        },
        disabled: {
          type: Boolean,
          default: false,
        },
        maxlength: Number,
        clearable: {
          type: Boolean,
          default: true,
        },
        size: String,
        placeholder: String,
        appendPort: {
          type: Boolean,
          default: false,
        },
        autocomplete: {
          default: '',
        },
        suffixIcon: {
          default: '',
        },
      },
      data() {
        return {
          input: null,
          inputEle: null,
        };
      },
      computed: {
        inputValue: {
          get() {
            return this.value;
          },
          set(value) {
            this.$emit('inputChange', value, this.field);
          },
        },
      },
      methods: {
        inputChange() {
          this.$emit('inputChange');
        },
        inputFun() {
          this.$emit('input');
        },
        focusInput(e) {
          this.inputEle = e.srcElement;
          // 关闭所有keyboard
          let arr = document.querySelectorAll('.hg-theme-default');
          arr.forEach((ele) => {
            ele.style.visibility = 'hidden';
          });
    
          // 打开当前输入框的keyboard
          let currentKeyborad = this.$refs[this.keyboardClass];
          currentKeyborad.$el.style.visibility = 'visible';
          this.$emit('focus');
        },
        onChange(input) {
          this.inputValue = input;
          // 解决当输入框为密码输入框时,切换显示/隐藏密码,光标在开头问题,注意:element-ui版本号需为2.15.2及以上
          this.inputEle.focus();
        },
        onKeyPress(button) {
          // console.log('onKeyPress', button);
        },
      },
    };
    </script>
    
    <style lang="less" scoped>
    @import 'style/less/var';
    @deep: ~'>>>';
    .input-keyboard {
      @{deep}.hg-theme-default {
        position: fixed;
        left: 50%;
        bottom: 20px;
        transform: translate(-50%);
        visibility: hidden;
        margin-top: 20px;
        z-index: 2000;
        // 中文文字选择框
        .hg-candidate-box {
          position: static;
          transform: translateY(0);
        }
        // 键盘
        .hg-rows {
        }
      }
      &.citc-search-input {
        .el-input {
           100% !important;
        }
      }
      // 密码输入框显示密码图标效果
      @{deep}.el-input__inner[type='text'] + span .el-icon-view {
        color: red;
      }
    }
    </style>

    2.3 登录页login.vue

    <template>
      <div class="login" @click="judgeCloseKeyboard">
        <div class="form-card" id="form-card">
          <div class="form-body">
            <el-form :model="form" ref="form" :rules="rules" @submit.native.prevent>
              <el-form-item prop="username">
                <keyboard-input
                  keyboardClass="login-username"   <== 注意:页面内有多个输入框时,每个输入框的keyboardClass必须是唯一的,才不会报错
                  field="username"
                  :value="form.username"
                  :maxlength="10"
                  clearable
                  @inputChange="formItemChange"
                  placeholder="用户名"
                ></keyboard-input>
              </el-form-item>
              <button
                class="btn-login"
                :disabled="loginDisabled"
                @click.prevent="submit('form')"
              >
                {{ isSubmitting ? '登录中' : '登录' }}
              </button>
            </el-form>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import { KeyboardInput } from '../../components/keyboard-input';
    import { keyboardInputMixin } from '@/util/mixins';
    import { judgeCloseKeyboard } from '@/util/core';
    
    export default {
      components: { KeyboardInput, LoginDialog },
      mixins: [keyboardInputMixin], 
      data() {
        return {
          judgeCloseKeyboard,
          isSubmitting: false,
          form: {
            username: '',
          },
          rules: {
            username: [
              { required: true, message: '请输入用户名', trigger: 'blur' },
            ],
          },
        };
      },
      computed: {
        loginDisabled() {
          return this.form.username === '' || this.isSubmitting;
        },
      },
      methods: {
        submit(formName) {
          this.$refs[formName].validate((valid, errFields) => {
            if (valid) {}
          });
        },
      },
    };
    </script>
    
    <style lang="less" scoped>
    @deep: ~'>>>';
    
    .login {
       100%;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      @{deep}.vue-touch-keyboard{
         600px;
      }
    }
    .form-card {
       550px;
      border-radius: 5px;
      background: rgba(255, 255, 255, 0.8);
      padding: 40px 120px 80px 120px;
      box-sizing: border-box;
      .btn-login {
         100%;
        height: 40px;
        background: #db3e5d;
        border-radius: 2px;
        border: none;
        color: rgba(255, 255, 255, 1);
        &:hover {
          cursor: pointer;
        }
        &:focus {
          outline: none;
        }
        &:disabled {
          cursor: not-allowed;
          background-color: rgba(219, 62, 93, 0.5);
        }
      }
    }
    @{deep}.el-form-item {
      margin-bottom: 30px;
      .el-input__inner {
        padding: 0 16px;
        font-size: 13px;
        border-radius: 2px;
        border-color: rgba(0, 0, 0, 0.15);
        &:hover,
        &:focus,
        &.is-active {
          border-color: #db3e5d;
        }
      }
    }
    </style> 

    2.4 core.js

    /**
     * 点击除输入框及虚拟键盘外元素时,隐藏虚拟键盘
     * @param  {e} 点击的元素
     */
    export const judgeCloseKeyboard = e => {
      e.stopPropagation(); // 阻止事件冒泡
      let arr = document.querySelectorAll('.input-keyboard'); // 设置目标区域
      let flag = false;
      const excludeClassName = [
        'input-keyboard', // 带虚拟键盘的输入框
        'el-input__inner', // 输入框
        'el-input__clear', // 输入框-清除图标
        'el-textarea__inner', // 输入框-textarea
        'hg-candidate-box', // 中文选择框
        'hg-candidate-box-prev', // 中文选择框-上一页
        'hg-candidate-box-list', // 中文选择框-内容列表
        'hg-candidate-box-list-item', // 中文选择框-内容选项
        'hg-candidate-box-next', // 中文选择框-下一页
        'hg-theme-default', // 虚拟键盘
        'hg-rows', // 虚拟键盘-内容
        'hg-row', // 虚拟键盘-行
        'hg-button' // 虚拟键盘-按钮
      ];
      let classList = e.target.classList.value.split(' ');
      let concatArr = excludeClassName.concat(classList);
    
      arr.forEach(ele => {
        // 判断点击事件发生在区域外的条件是:1. 点击事件的对象不是目标区域本身 2. 事件对象同时也不是目标区域的子元素
        if (
          // 判断当前点击的元素类名,是否包含排除元素
          new Set(concatArr).size === concatArr.length &&
          !ele.contains(e.target)
        ) {
          flag = true;
        }
      });
      flag && hideKeyboard();
    };
    
    /**
     * 隐藏虚拟键盘
     */
    export const hideKeyboard = () => {
      let arr = document.querySelectorAll('.hg-theme-default');
      arr.forEach(ele => {
        ele.style.visibility = 'hidden';
      });
    };

    2.5 mixins.js

    // 虚拟键盘输入公用方法
    export const keyboardInputMixin = {
      methods: {
        // 对部分表单字段进行校验
        validataForm(field, formname = 'form') {
          this.$refs[formname].validateField(field);
        },
        // 虚拟键盘输入
        formItemChange(val, formItemField) {
          this.form[formItemField] = val;
          this.validataForm(formItemField);
        },
      },
    };

     

    3.遇到的问题

    3.1 列表可复合搜索,搜索部分是组件ref为search,虚拟键盘输入后,直接修改输入框绑定的值,发现变量值改变,但页面未更新

    解决方法

    使用$set方法修改复合搜索条件值

     keyWordsChange(val) {
       this.$set(this.$refs.search.form, 'keyWords', val); // 响应式对象修改数据
      ...
    }

     3.2 表单内有两个输入框表单项,切换radio通过v-if判断显示其中一个,发现在切换radio后,虚拟键盘的样式错乱

    解决方法

    使用v-show判断,但我的两个表单项是必填的,校验时就有问题,这个问题可通过单独给两个表单项写校验规则解决

    ...
    <el-form-item
            v-show="form.typeRadio=== 1"
            label="名称:"
            prop="name"
            class="is-required"    <==  必填图标
          >
            <keyboard-input
              keyboardClass="name"
              :value="form.name"
              :maxlength="20"
              clearable
              @inputChange="nameChange"
              placeholder="20位字符以内"
            ></keyboard-input>
    </el-form-item>
    
    ...
    
    
    const validateName = (rule, value, callback) => {
        if (this.form.typeRadio === 1) {
            if (value === '') {
              return callback(new Error('请输入名称'));
            } else {
              callback();
            }
        } else {
            callback();
        }
    };
    ...
  • 相关阅读:
    史上最全Html与CSS布局技巧
    Discuz! X的CSS加载机制
    关于input框只能输入纯数字问题
    对象的数据属性
    vue中将光标定位到Input上的问题
    端口占用问题解决方案
    el-button如何消除右边计数样式
    如何改变vscode的背景颜色
    为什么——要实例化对象?
    call()&apply()
  • 原文地址:https://www.cnblogs.com/linjiangxian/p/16223681.html
Copyright © 2020-2023  润新知