Vue3 question: How to implement a 6-digit payment password input box?

Editing typesetting  | Song Dashi

Platform Operation  |

ONE problem description

April 25, 2023, long time no see everyone.

What I want to share with you today is how to implement the 6-digit payment password input box component.

Because the web side is not like the mobile side, it does not have a mature and packaged 6-digit payment password input box UI component, so we need to handle it ourselves, and implement it in terms of layout, style, function, etc. Here we do it Take a note.

Specific requirements: In the operation column of the customer information form, click the button to modify the payment password, and it will jump to the component page of the 6-digit payment password input box. At the same time, the cipher text in the input box is required to be displayed, non-editable, non-returnable, and displayed immediately; when it reaches 6 digits, it will automatically enter the confirmation payment password; when the payment password reaches 6 digits, it will automatically check the consistency of the two input passwords and display OK button. This function is used in the bank, the customer enters the password with the device, the teller cannot see the password, but the teller can prompt the operation.

Specific questions: 1. How to realize the ciphertext display, and each box can only input 1 digit; 2. How to realize that the input box cannot be edited or rolled back; 3. How to check the consistency of the two input passwords; 4. What should I do if my business needs to restrict keyboard keys?

Today, in this article, we use the most concise language to properly understand the above issues.

TWO problem solving 

1. Code overview

The code to implement the 6-digit payment password input box component is as follows, just copy it and use it directly!

<template>
  <div style="display: flex; flex-direction: column">
    <!-- 密码输入框 -->
    <div class="input-box" style="margin: auto">
      <!-- 输入密码 -->
      <div style="font-size: 20px; margin-bottom: 5px">{
   
   { "输入密码" }}</div>
      <div class="input-content" @keyup="keyup" @input="inputEvent">
        <input max="9" min="0" maxlength="1" data-index="0" v-model.number="state.input[0]" type="password"
          ref="firstinput" :disabled="state.disabledInput[0]" />
        <input max="9" min="0" maxlength="1" data-index="1" v-model.number="state.input[1]" type="password"
          :disabled="state.disabledInput[1]" />
        <input max="9" min="0" maxlength="1" data-index="2" v-model.number="state.input[2]" type="password"
          :disabled="state.disabledInput[2]" />
        <input max="9" min="0" maxlength="1" data-index="3" v-model.number="state.input[3]" type="password"
          :disabled="state.disabledInput[3]" />
        <input max="9" min="0" maxlength="1" data-index="4" v-model.number="state.input[4]" type="password"
          :disabled="state.disabledInput[4]" />
        <input max="9" min="0" maxlength="1" data-index="5" v-model.number="state.input[5]" type="password"
          :disabled="state.disabledInput[5]" />
      </div>

      <!-- 确认密码 -->
      <div style="margin-top: 30px; font-size: 20px; margin-bottom: 5px">{
   
   { "确认密码" }}</div>
      <div class="input-content" @keyup="confirmKeyUp" @input="confirmInputEvent">
        <input max="9" min="0" maxlength="1" data-index="0" v-model.number="state.confirmInput[0]" type="password"
          ref="confirmfirstinput" :disabled="state.disabledConfirmInput[0]" />
        <input max="9" min="0" maxlength="1" data-index="1" v-model.number="state.confirmInput[1]" type="password"
          :disabled="state.disabledConfirmInput[1]" />
        <input max="9" min="0" maxlength="1" data-index="2" v-model.number="state.confirmInput[2]" type="password"
          :disabled="state.disabledConfirmInput[2]" />
        <input max="9" min="0" maxlength="1" data-index="3" v-model.number="state.confirmInput[3]" type="password"
          :disabled="state.disabledConfirmInput[3]" />
        <input max="9" min="0" maxlength="1" data-index="4" v-model.number="state.confirmInput[4]" type="password"
          :disabled="state.disabledConfirmInput[4]" />
        <input max="9" min="0" maxlength="1" data-index="5" v-model.number="state.confirmInput[5]" type="password"
          :disabled="state.disabledConfirmInput[5]" />
      </div>
    </div>

    <!-- 按钮 -->
    <div style="margin: auto; margin-top: 30px">
      <el-button type="info" :disabled="state.disabledConfirm" @click="reConfirm"
        :class="[state.disabledConfirm ? 'noActive' : 'active']">{
   
   { "确定" }}</el-button>
      <el-button type="warning" @click="reset">{
   
   { "重新输入" }}</el-button>
    </div>

    <!-- 提示区 -->
    <div
      style="width: 500px; height: 200px; padding: 10px; border: 1px solid #ccc; margin: auto; margin-top: 30px; color: red; text-align: center; font-size: 24px">
      <p>{
   
   { state.tipContent }}</p>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { nextTick, reactive, ref, onMounted } from "vue";
import { ElMessage, ElMessageBox } from 'element-plus'

const state = reactive({
  // 输入数组
  input: ["", "", "", "", "", ""],
  // 确认输入数组
  confirmInput: ["", "", "", "", "", ""],
  // 存放粘贴进来的数字
  pasteResult: [],
  confirmPasteResult: [],
  // 一上来禁用确定按钮
  disabledConfirm: true,
  // 输入框是否禁用
  disabledInput: [false, false, false, false, false, false],
  disabledConfirmInput: [false, false, false, false, false, false],
  // 提示内容
  tipContent: "请告知客户输入6位数字密码,输入完毕后,点击回车确认。"
})
// 获取第一个元素的ref
const firstinput = ref()
const confirmfirstinput = ref()

// 页面一加载就使第一个框聚焦
onMounted(() => {
  // 等待dom渲染完成,在执行focus,否则无法获取到焦点
  nextTick(() => {
    firstinput.value.focus();
  });
})

// @input的处理方法
// 解决一个输入框输入多个字符
const inputEvent = (e) => {
  var index = e.target.dataset.index * 1;
  var el = e.target;

  // 限制只能输入数字
  el.value = el.value.replace(/[^\d]/g, "");

  if (el.value.length >= 1) {
    // 密文显示、不可编辑、不可回退、即时显示
    state.disabledInput[index] = true;
    if (el.nextElementSibling) {
      el.nextElementSibling.focus();
    }
  }

  // 到达6位数,自动进入确认支付密码
  if (!el.nextElementSibling) {
    confirmfirstinput.value.focus();
    state.tipContent = "请告知客户再次输入6位数字密码,输入完毕后,点击回车确认。";
  }
}
// @keydown的处理方法,根据业务需要添加
// 此示例没有使用
const keydown = (e) => {
  var index = e.target.dataset.index * 1;
  var el = e.target;
  // 回退键
  if (e.key === 'Backspace') {
    if (state.input[index].length > 0) {
      state.input[index] = ''
    } else {
      if (el.previousElementSibling) {
        el.previousElementSibling.focus()
        state.input[index - 1] = ''
      }
    }
  }
  // 删除键 
  else if (e.key === 'Delete') {
    if (state.input[index].length > 0) {
      state.input[index] = ''
    } else {
      if (el.nextElementSibling) {
        state.input[1] = ''
      }
    }
    if (el.nextElementSibling) {
      el.nextElementSibling.focus()
    }
  }
  // 左键
  else if (e.key === 'ArrowLeft') {
    if (el.previousElementSibling) {
      el.previousElementSibling.focus()
    }
  }
  // 右键 
  else if (e.key === 'ArrowRight') {
    if (el.nextElementSibling) {
      el.nextElementSibling.focus()
    }
  }
  // 上键 
  else if (e.key === 'ArrowUp') {
    if (Number(state.input[index]) * 1 < 9) {
      state.input[index] = (Number(state.input[index]) * 1 + 1).toString()
    }
  }
  // 下键  
  else if (e.key === 'ArrowDown') {
    if (Number(state.input[index]) * 1 > 0) {
      state.input[index] = (Number(state.input[index]) * 1 - 1).toString()
    }
  }
}
// @keyup的处理方法
const keyup = (e) => {
  var index = e.target.dataset.index * 1;
  // 如果为最后一个框,则输入框全部失焦
  if (index === 5) {
    if (state.input.join("").length === 6) {
      document.activeElement.blur();
    }
  }

}

// @input的处理方法
// 解决一个输入框输入多个字符
const confirmInputEvent = (e) => {
  var index = e.target.dataset.index * 1;
  var el = e.target;

  if (el.value.length >= 1) {
    // 密文显示、不可编辑、不可回退、即时显示
    state.disabledConfirmInput[index] = true;
    if (el.nextElementSibling) {
      el.nextElementSibling.focus();
    }
  }

  // 到达6位数,自动检验两次输入密码的一致性
  if (!el.nextElementSibling) {
    // 一一比较元素值,有一个不相等就不等
    for (let i = 0; i < state.input.length; i++) {
      if (state.input[i] !== state.confirmInput[i]) {
        state.tipContent = "请告知客户两次密码输入不一致,柜员点击重新输入,清空密码后请告知客户重新输入。";
        return;
      }
    }

    state.tipContent = "密码合规,点击确定按钮进行修改。";
    // 确定按钮变为可用
    state.disabledConfirm = false;
  }
}
// @keydown的处理方法,根据业务需要添加
// 此示例没有使用
const confirmKeydown = (e) => {
  var index = e.target.dataset.index * 1;
  var el = e.target;
  // 回退键
  if (e.key === 'Backspace') {
    if (state.confirmInput[index].length > 0) {
      state.confirmInput[index] = ''
    } else {
      if (el.previousElementSibling) {
        el.previousElementSibling.focus()
        state.confirmInput[index - 1] = ''
      }
    }
  }
  // 删除键 
  else if (e.key === 'Delete') {
    if (state.confirmInput[index].length > 0) {
      state.confirmInput[index] = ''
    } else {
      if (el.nextElementSibling) {
        state.confirmInput[1] = ''
      }
    }
    if (el.nextElementSibling) {
      el.nextElementSibling.focus()
    }
  }
  // 左键
  else if (e.key === 'ArrowLeft') {
    if (el.previousElementSibling) {
      el.previousElementSibling.focus()
    }
  }
  // 右键 
  else if (e.key === 'ArrowRight') {
    if (el.nextElementSibling) {
      el.nextElementSibling.focus()
    }
  }
  // 上键 
  else if (e.key === 'ArrowUp') {
    if (Number(state.confirmInput[index]) * 1 < 9) {
      state.confirmInput[index] = (Number(state.confirmInput[index]) * 1 + 1).toString()
    }
  }
  // 下键  
  else if (e.key === 'ArrowDown') {
    if (Number(state.confirmInput[index]) * 1 > 0) {
      state.confirmInput[index] = (Number(state.confirmInput[index]) * 1 - 1).toString()
    }
  }
}
// @keyup的处理方法
const confirmKeyUp = (e) => {
  var index = e.target.dataset.index * 1;
  // 如果为最后一个框,则输入框全部失焦
  if (index === 5) {
    if (state.confirmInput.join("").length === 6) {
      document.activeElement.blur();
    }
  }
}

// 重新输入
const reset = () => {
  state.disabledConfirm = true;
  state.tipContent = "请告知客户输入6位数字密码,输入完毕后,点击回车确认。";

  state.input = ["", "", "", "", "", ""];
  state.confirmInput = ["", "", "", "", "", ""];
  state.disabledInput = [false, false, false, false, false, false];
  state.disabledConfirmInput = [false, false, false, false, false, false];

  // 等待dom渲染完成,在执行focus,否则无法获取到焦点
  nextTick(() => {
    firstinput.value.focus();
  });
}

// 确认修改
const reConfirm = () => {
  ElMessageBox.confirm(
    '是否确定修改?',
    '温馨提示',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    }
  )
    .then(() => {
      // 此处调修改支付密码接口

      ElMessage({
        type: 'success',
        message: '修改成功!',
      })
    })
    .catch(() => {
      ElMessage({
        type: 'info',
        message: '已取消修改!',
      })
    })
}
</script>

<style lang="scss" scoped>
.input-box {
  .input-content {
    width: 512px;
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: space-between;

    input {
      color: inherit;
      font-family: inherit;
      border: 0;
      outline: 0;
      border-bottom: 1px solid #919191;
      height: 60px;
      width: 60px;
      font-size: 44px;
      text-align: center;
    }
  }

  input::-webkit-outer-spin-button,
  input::-webkit-inner-spin-button {
    appearance: none;
    margin: 0;
  }
}

.noActive {
  color: #fff !important;
  border-width: 0px !important;
  background-color: #ccc !important;
}

.active {
  color: #fff !important;
  border-width: 0px !important;
  background-color: #67c23a !important;
}
</style>

2. Problem analysis

1. Question: How to realize the ciphertext display, and only one digit can be entered in each box?

Answer: To realize ciphertext display, just change the type of the input box to password. For the realization that each box can only enter 1 digit, the effect of only using the maxlength attribute of the input box here is not perfect, and there may be unrestricted situations. It is necessary to judge the length of the current element value in the @input event. If it is greater than If it is equal to 1, use nextElementSibling.focus() to focus the cursor on the next sibling element.

2. Question: How to realize that the input box cannot be edited or rolled back?

Answer: The disabled attribute of the input box is used. In the @input event, the disabled attribute of the current input element can be changed to true. Here, the disabled attribute values ​​of the input boxes are stored in an array, in order to facilitate subsequent acquisition and modification.


3. Question: How to check the consistency of the two input passwords?

Answer: The simplest for loop is used to traverse the input password array and confirmation password array, and compare their element values ​​one by one. If one is not equal, it will not be equal, and the execution of the entire function will be terminated by return;.

4. Question: What should I do if my business needs to restrict keyboard keys?

Answer: You can add @keydown or @keyup events to the input box, and do some business processing on different keys by judging the key inside the callback.

- END -

Guess you like

Origin blog.csdn.net/m0_74802419/article/details/130396572