预览图

image.png
image.png

组件源码

父组件

  1. <template>
  2. <div class="google-verify-content">
  3. <p>{{ props.title }}</p>
  4. <GoogleCode @finishCode="getGoogleCode" />
  5. <div class="login-modal-footer">
  6. <n-button
  7. type="tertiary"
  8. class="login-modal-btn"
  9. @click="$emit('closeModal')"
  10. >取消</n-button
  11. >
  12. <n-button
  13. type="primary"
  14. class="login-modal-btn"
  15. @click="submitCallback"
  16. >{{ props.confirmText }}</n-button
  17. >
  18. </div>
  19. </div>
  20. </template>
  21. <script lang="ts" setup>
  22. import { ref } from "vue";
  23. const googleCode = ref("");
  24. let emit = defineEmits(["submitCallback", "closeModal"]);
  25. let props = defineProps({
  26. title: {
  27. type: String,
  28. default: "为保障系统信息和商户资金安全,请您进行谷歌验证",
  29. },
  30. confirmText: {
  31. type: String,
  32. default: "保存",
  33. },
  34. });
  35. function submitCallback() {
  36. emit("submitCallback", googleCode.value);
  37. }
  38. function getGoogleCode(val) {
  39. googleCode.value = val;
  40. }
  41. </script>

核心子组件-输入窗口

初始版本

  1. <!--
  2. * @Date: 2022-03-02 14:20:28
  3. * @LastEditTime: 2022-03-09 17:36:41
  4. -->
  5. <template>
  6. <div v-bind="$attrs" class="goole-code-box">
  7. <div id="google-input" class="google-input-content" @click="state.canInput = true">
  8. <input
  9. class="input-item"
  10. :class="{ active: state.canInput && state.inputCodeArr.length === index }"
  11. v-for="(item, index) in state.inputLengthArr"
  12. :key="index"
  13. type="text"
  14. maxlength="1"
  15. @input="onChange($event, index)"
  16. @focus="state.focus = true"
  17. @blur="state.focus = false"
  18. v-model="item.content"
  19. />
  20. <slot name="get-code-btn"></slot>
  21. </div>
  22. </div>
  23. </template>
  24. <script lang="ts" setup>
  25. import { reactive, onMounted, onBeforeUnmount, watchEffect } from "vue";
  26. import _ from "lodash";
  27. let emit = defineEmits(["setGooglecode", "finishCode"]);
  28. let state = reactive({
  29. inputCodeArr: [],
  30. canInput: false,
  31. focus: false,
  32. inputLengthArr: [
  33. {
  34. id: 0,
  35. content: "",
  36. },
  37. {
  38. id: 1,
  39. content: "",
  40. },
  41. {
  42. id: 2,
  43. content: "",
  44. },
  45. {
  46. id: 3,
  47. content: "",
  48. },
  49. {
  50. id: 4,
  51. content: "",
  52. },
  53. {
  54. id: 5,
  55. content: "",
  56. },
  57. ],
  58. });
  59. let keyMap = {
  60. 48: "0",
  61. 49: "1",
  62. 50: "2",
  63. 51: "3",
  64. 52: "4",
  65. 53: "5",
  66. 54: "6",
  67. 55: "7",
  68. 56: "8",
  69. 57: "9",
  70. };
  71. let props = defineProps({
  72. status: {
  73. type: Boolean,
  74. default: true,
  75. },
  76. });
  77. watchEffect(() => {
  78. if (state.inputCodeArr.length === 0) {
  79. state.inputLengthArr.forEach((item) => (item.content = ""));
  80. }
  81. if (!props.status && !state.focus) {
  82. console.log("失焦处理");
  83. state.canInput = false; //外部状态禁用
  84. }
  85. if (state.inputCodeArr.length) {
  86. state.inputLengthArr.forEach((item) => {
  87. if (item.id === state.inputCodeArr.length - 1) {
  88. item.content = state.inputCodeArr[item.id];
  89. }
  90. if (item.id > state.inputCodeArr.length - 1) {
  91. item.content = "";
  92. }
  93. });
  94. let outPut = state.inputCodeArr.join("");
  95. if (state.inputCodeArr.length === 6) {
  96. state.canInput = false;
  97. console.log("finishCode", outPut);
  98. emit("finishCode", outPut);
  99. }
  100. }
  101. });
  102. onMounted(() => {
  103. document.onkeydown = (e) => {
  104. var keyNum = window.event ? e.keyCode : e.which;
  105. if (!state.canInput) {
  106. return;
  107. }
  108. if (keyNum === 8) {
  109. // 删除键盘
  110. state.inputCodeArr.pop();
  111. let outPut = state.inputCodeArr.join("");
  112. console.log(outPut, "outPut");
  113. emit("setGooglecode", outPut);
  114. return;
  115. }
  116. if (state.inputCodeArr.length === 6) {
  117. state.canInput = false;
  118. return;
  119. }
  120. // 数字监听
  121. if (keyMap[keyNum] && state.inputCodeArr.length <= 6) {
  122. console.log("你输入了" + keyMap[keyNum], state.inputLengthArr);
  123. state.inputCodeArr.push(keyMap[keyNum]);
  124. let outPut = state.inputCodeArr.join("");
  125. emit("setGooglecode", outPut);
  126. }
  127. };
  128. });
  129. onBeforeUnmount(() => {
  130. document.onkeydown = null;
  131. });
  132. const onChange = (event, index) => {
  133. console.log(event, index);
  134. };
  135. </script>
  • fixed 双数组难以维护的问题
  • 聚焦后无法让下个数组窗口自动聚焦
  • 输入完毕后,修改之前输入窗口的值, 聚焦值和改动值同时改变

版本v1.01

  1. <!--
  2. * @Date: 2022-03-02 14:20:28
  3. * @LastEditTime: 2022-04-01 16:37:11
  4. -->
  5. <template>
  6. <div v-bind="$attrs" class="goole-code-box">
  7. <div
  8. id="google-input"
  9. class="google-input-content"
  10. >
  11. <input
  12. class="input-item"
  13. :id="nameId(index)"
  14. :class="{ active: state.focusIndex === index }"
  15. v-for="(item, index) in state.inputLengthArr"
  16. type="text"
  17. maxlength="1"
  18. @input="onChange($event, index)"
  19. @focus="getFocus($event, index)"
  20. v-model="item.content"
  21. />
  22. <slot name="get-code-btn"></slot>
  23. </div>
  24. </div>
  25. </template>
  26. <script lang="ts" setup>
  27. import { reactive } from "vue";
  28. import _ from "lodash";
  29. let emit = defineEmits(["finishCode"]);
  30. let state = reactive({
  31. focusIndex: 0,
  32. inputLengthArr: [
  33. {
  34. id: 0,
  35. content: "",
  36. },
  37. {
  38. id: 1,
  39. content: "",
  40. },
  41. {
  42. id: 2,
  43. content: "",
  44. },
  45. {
  46. id: 3,
  47. content: "",
  48. },
  49. {
  50. id: 4,
  51. content: "",
  52. },
  53. {
  54. id: 5,
  55. content: "",
  56. },
  57. ],
  58. });
  59. /**
  60. * @description: 聚焦问题修改
  61. * @param {*} event
  62. * @param {*} index
  63. * @return {*}
  64. */
  65. function getFocus(event, index) {
  66. state.focusIndex = index;
  67. }
  68. function submitGoogle() {
  69. let virfyLength = _.sumBy(state.inputLengthArr, "content");
  70. console.log("finishCode", virfyLength);
  71. emit("finishCode", virfyLength);
  72. }
  73. /**
  74. * @description: 焦点自动获取优化
  75. * @param {*} event
  76. * @param {*} index
  77. * @return {*}
  78. */
  79. function onChange(event, index) {
  80. if (event.data) {
  81. document.getElementById(nameId(index + 1))?.focus();
  82. state.focusIndex = index + 1;
  83. } else {
  84. if (index === 0) return;
  85. document.getElementById(nameId(index - 1))?.focus();
  86. state.focusIndex = index - 1;
  87. }
  88. let virfyLength = _.sumBy(state.inputLengthArr, "content");
  89. if (virfyLength.length === 6) {
  90. submitGoogle();
  91. }
  92. }
  93. function nameId(index) {
  94. return "googel-input" + index;
  95. }
  96. </script>
  97. <style lang="scss" scoped>
  98. .goole-code-box {
  99. width: 100%;
  100. .google-input-content {
  101. display: flex;
  102. justify-content: space-between;
  103. flex-wrap: nowrap;
  104. input.input-item {
  105. background: var(--n-close-color-hover);
  106. border-radius: 8px 8px 8px 8px;
  107. opacity: 0.3;
  108. text-align: center;
  109. outline: none;
  110. height: 0.75rem;
  111. width: 0.75rem;
  112. font-size: 30px;
  113. color: #ffffff;
  114. border: none;
  115. }
  116. input.active {
  117. border: 2px solid $default-primary !important;
  118. -moz-box-shadow: 1px 1px 7px var(--n-color-hover);
  119. -webkit-box-shadow: 1px 1px 7px var(--n-color-hover);
  120. box-shadow: 1px 1px 7px var(--n-color-hover);
  121. }
  122. input::-webkit-outer-spin-button,
  123. input::-webkit-inner-spin-button {
  124. // chrome
  125. -webkit-appearance: none;
  126. appearance: none;
  127. margin: 0;
  128. }
  129. input {
  130. // 火狐
  131. -moz-appearance: textfield;
  132. }
  133. }
  134. }
  135. </style>

image.png

  • v1.01保证正常功能使用,但在实际场景中存在使用问题
  • 没有一键复制功能

版本v2.01

  1. <template>
  2. <div v-bind="$attrs" class="goole-code-box">
  3. <div
  4. id="google-input"
  5. class="google-input-content"
  6. >
  7. <input
  8. class="input-item"
  9. :id="nameId(index)"
  10. :class="{ active: state.focusIndex === index }"
  11. v-for="(item, index) in state.inputLengthArr"
  12. type="text"
  13. maxlength="1"
  14. autocomplete="off"
  15. :onkeydown="onkeydown"
  16. @input="onChange($event, index)"
  17. @focus="getFocus($event, index)"
  18. v-model="item.content"
  19. />
  20. <slot name="get-code-btn"></slot>
  21. </div>
  22. </div>
  23. </template>
  1. <script lang="ts" setup>
  2. import { reactive } from "vue";
  3. import { readClipboard, clearClipboard } from "@/utils/copy";
  4. import _ from "lodash";
  5. let emit = defineEmits(["finishCode"]);
  6. let state = reactive({
  7. focusIndex: 0,
  8. inputLengthArr: [
  9. {
  10. id: 0,
  11. content: "",
  12. },
  13. {
  14. id: 1,
  15. content: "",
  16. },
  17. {
  18. id: 2,
  19. content: "",
  20. },
  21. {
  22. id: 3,
  23. content: "",
  24. },
  25. {
  26. id: 4,
  27. content: "",
  28. },
  29. {
  30. id: 5,
  31. content: "",
  32. },
  33. ],
  34. });
  35. clearClipboard();
  36. function readCode() {
  37. readClipboard()
  38. .then((res) => {
  39. let boardStr = res;
  40. if (boardStr.length === 6) {
  41. emit("finishCode", boardStr);
  42. state.inputLengthArr.forEach((item, index) => {
  43. item.content = boardStr.charAt(index);
  44. });
  45. }
  46. })
  47. .catch((err) => console.warn(err));
  48. }
  49. /**
  50. * @description: 聚焦问题修改
  51. * @param {*} event
  52. * @param {*} index
  53. * @return {*}
  54. */
  55. function getFocus(event:Event, index:number) {
  56. if (index === 0) {
  57. readCode();
  58. }
  59. state.focusIndex = index;
  60. }
  61. function onkeydown(event:Event) {
  62. if (state.focusIndex === 0) return true;
  63. if( event.keyCode === 8) {
  64. clearClipboard();
  65. }
  66. console.log(event.keyCode , state.inputLengthArr[state.focusIndex]);
  67. if (
  68. state.inputLengthArr[state.focusIndex].content === "" &&
  69. event.keyCode === 8
  70. ) {
  71. document.getElementById(nameId(state.focusIndex - 1))?.focus();
  72. state.focusIndex = state.focusIndex - 1;
  73. }
  74. }
  75. function submitGoogle() {
  76. let virfyLength = _.sumBy(state.inputLengthArr, "content");
  77. emit("finishCode", virfyLength);
  78. }
  79. /**
  80. * @description: 焦点自动获取优化
  81. * @param {*} event
  82. * @param {*} index
  83. * @return {*}
  84. */
  85. function onChange(event, index) {
  86. if (event.data) {
  87. document.getElementById(nameId(index + 1))?.focus();
  88. state.focusIndex = index + 1;
  89. } else {
  90. if (index === 0) return;
  91. document.getElementById(nameId(index - 1))?.focus();
  92. state.focusIndex = index - 1;
  93. }
  94. let virfyLength = _.sumBy(state.inputLengthArr, "content");
  95. if (virfyLength.length === 6) {
  96. submitGoogle();
  97. }
  98. }
  99. function nameId(index) {
  100. return "googel-input" + index;
  101. }
  102. </script>

依赖自定义工具函数-一键复制(原生)
关于复制到粘贴板