预览图
组件源码
父组件
<template>
<div class="google-verify-content">
<p>{{ props.title }}</p>
<GoogleCode @finishCode="getGoogleCode" />
<div class="login-modal-footer">
<n-button
type="tertiary"
class="login-modal-btn"
@click="$emit('closeModal')"
>取消</n-button
>
<n-button
type="primary"
class="login-modal-btn"
@click="submitCallback"
>{{ props.confirmText }}</n-button
>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
const googleCode = ref("");
let emit = defineEmits(["submitCallback", "closeModal"]);
let props = defineProps({
title: {
type: String,
default: "为保障系统信息和商户资金安全,请您进行谷歌验证",
},
confirmText: {
type: String,
default: "保存",
},
});
function submitCallback() {
emit("submitCallback", googleCode.value);
}
function getGoogleCode(val) {
googleCode.value = val;
}
</script>
核心子组件-输入窗口
初始版本
<!--
* @Date: 2022-03-02 14:20:28
* @LastEditTime: 2022-03-09 17:36:41
-->
<template>
<div v-bind="$attrs" class="goole-code-box">
<div id="google-input" class="google-input-content" @click="state.canInput = true">
<input
class="input-item"
:class="{ active: state.canInput && state.inputCodeArr.length === index }"
v-for="(item, index) in state.inputLengthArr"
:key="index"
type="text"
maxlength="1"
@input="onChange($event, index)"
@focus="state.focus = true"
@blur="state.focus = false"
v-model="item.content"
/>
<slot name="get-code-btn"></slot>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, onMounted, onBeforeUnmount, watchEffect } from "vue";
import _ from "lodash";
let emit = defineEmits(["setGooglecode", "finishCode"]);
let state = reactive({
inputCodeArr: [],
canInput: false,
focus: false,
inputLengthArr: [
{
id: 0,
content: "",
},
{
id: 1,
content: "",
},
{
id: 2,
content: "",
},
{
id: 3,
content: "",
},
{
id: 4,
content: "",
},
{
id: 5,
content: "",
},
],
});
let keyMap = {
48: "0",
49: "1",
50: "2",
51: "3",
52: "4",
53: "5",
54: "6",
55: "7",
56: "8",
57: "9",
};
let props = defineProps({
status: {
type: Boolean,
default: true,
},
});
watchEffect(() => {
if (state.inputCodeArr.length === 0) {
state.inputLengthArr.forEach((item) => (item.content = ""));
}
if (!props.status && !state.focus) {
console.log("失焦处理");
state.canInput = false; //外部状态禁用
}
if (state.inputCodeArr.length) {
state.inputLengthArr.forEach((item) => {
if (item.id === state.inputCodeArr.length - 1) {
item.content = state.inputCodeArr[item.id];
}
if (item.id > state.inputCodeArr.length - 1) {
item.content = "";
}
});
let outPut = state.inputCodeArr.join("");
if (state.inputCodeArr.length === 6) {
state.canInput = false;
console.log("finishCode", outPut);
emit("finishCode", outPut);
}
}
});
onMounted(() => {
document.onkeydown = (e) => {
var keyNum = window.event ? e.keyCode : e.which;
if (!state.canInput) {
return;
}
if (keyNum === 8) {
// 删除键盘
state.inputCodeArr.pop();
let outPut = state.inputCodeArr.join("");
console.log(outPut, "outPut");
emit("setGooglecode", outPut);
return;
}
if (state.inputCodeArr.length === 6) {
state.canInput = false;
return;
}
// 数字监听
if (keyMap[keyNum] && state.inputCodeArr.length <= 6) {
console.log("你输入了" + keyMap[keyNum], state.inputLengthArr);
state.inputCodeArr.push(keyMap[keyNum]);
let outPut = state.inputCodeArr.join("");
emit("setGooglecode", outPut);
}
};
});
onBeforeUnmount(() => {
document.onkeydown = null;
});
const onChange = (event, index) => {
console.log(event, index);
};
</script>
- fixed 双数组难以维护的问题
- 聚焦后无法让下个数组窗口自动聚焦
- 输入完毕后,修改之前输入窗口的值, 聚焦值和改动值同时改变
版本v1.01
<!--
* @Date: 2022-03-02 14:20:28
* @LastEditTime: 2022-04-01 16:37:11
-->
<template>
<div v-bind="$attrs" class="goole-code-box">
<div
id="google-input"
class="google-input-content"
>
<input
class="input-item"
:id="nameId(index)"
:class="{ active: state.focusIndex === index }"
v-for="(item, index) in state.inputLengthArr"
type="text"
maxlength="1"
@input="onChange($event, index)"
@focus="getFocus($event, index)"
v-model="item.content"
/>
<slot name="get-code-btn"></slot>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive } from "vue";
import _ from "lodash";
let emit = defineEmits(["finishCode"]);
let state = reactive({
focusIndex: 0,
inputLengthArr: [
{
id: 0,
content: "",
},
{
id: 1,
content: "",
},
{
id: 2,
content: "",
},
{
id: 3,
content: "",
},
{
id: 4,
content: "",
},
{
id: 5,
content: "",
},
],
});
/**
* @description: 聚焦问题修改
* @param {*} event
* @param {*} index
* @return {*}
*/
function getFocus(event, index) {
state.focusIndex = index;
}
function submitGoogle() {
let virfyLength = _.sumBy(state.inputLengthArr, "content");
console.log("finishCode", virfyLength);
emit("finishCode", virfyLength);
}
/**
* @description: 焦点自动获取优化
* @param {*} event
* @param {*} index
* @return {*}
*/
function onChange(event, index) {
if (event.data) {
document.getElementById(nameId(index + 1))?.focus();
state.focusIndex = index + 1;
} else {
if (index === 0) return;
document.getElementById(nameId(index - 1))?.focus();
state.focusIndex = index - 1;
}
let virfyLength = _.sumBy(state.inputLengthArr, "content");
if (virfyLength.length === 6) {
submitGoogle();
}
}
function nameId(index) {
return "googel-input" + index;
}
</script>
<style lang="scss" scoped>
.goole-code-box {
width: 100%;
.google-input-content {
display: flex;
justify-content: space-between;
flex-wrap: nowrap;
input.input-item {
background: var(--n-close-color-hover);
border-radius: 8px 8px 8px 8px;
opacity: 0.3;
text-align: center;
outline: none;
height: 0.75rem;
width: 0.75rem;
font-size: 30px;
color: #ffffff;
border: none;
}
input.active {
border: 2px solid $default-primary !important;
-moz-box-shadow: 1px 1px 7px var(--n-color-hover);
-webkit-box-shadow: 1px 1px 7px var(--n-color-hover);
box-shadow: 1px 1px 7px var(--n-color-hover);
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
// chrome
-webkit-appearance: none;
appearance: none;
margin: 0;
}
input {
// 火狐
-moz-appearance: textfield;
}
}
}
</style>
- v1.01保证正常功能使用,但在实际场景中存在使用问题
- 没有一键复制功能
版本v2.01
<template>
<div v-bind="$attrs" class="goole-code-box">
<div
id="google-input"
class="google-input-content"
>
<input
class="input-item"
:id="nameId(index)"
:class="{ active: state.focusIndex === index }"
v-for="(item, index) in state.inputLengthArr"
type="text"
maxlength="1"
autocomplete="off"
:onkeydown="onkeydown"
@input="onChange($event, index)"
@focus="getFocus($event, index)"
v-model="item.content"
/>
<slot name="get-code-btn"></slot>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive } from "vue";
import { readClipboard, clearClipboard } from "@/utils/copy";
import _ from "lodash";
let emit = defineEmits(["finishCode"]);
let state = reactive({
focusIndex: 0,
inputLengthArr: [
{
id: 0,
content: "",
},
{
id: 1,
content: "",
},
{
id: 2,
content: "",
},
{
id: 3,
content: "",
},
{
id: 4,
content: "",
},
{
id: 5,
content: "",
},
],
});
clearClipboard();
function readCode() {
readClipboard()
.then((res) => {
let boardStr = res;
if (boardStr.length === 6) {
emit("finishCode", boardStr);
state.inputLengthArr.forEach((item, index) => {
item.content = boardStr.charAt(index);
});
}
})
.catch((err) => console.warn(err));
}
/**
* @description: 聚焦问题修改
* @param {*} event
* @param {*} index
* @return {*}
*/
function getFocus(event:Event, index:number) {
if (index === 0) {
readCode();
}
state.focusIndex = index;
}
function onkeydown(event:Event) {
if (state.focusIndex === 0) return true;
if( event.keyCode === 8) {
clearClipboard();
}
console.log(event.keyCode , state.inputLengthArr[state.focusIndex]);
if (
state.inputLengthArr[state.focusIndex].content === "" &&
event.keyCode === 8
) {
document.getElementById(nameId(state.focusIndex - 1))?.focus();
state.focusIndex = state.focusIndex - 1;
}
}
function submitGoogle() {
let virfyLength = _.sumBy(state.inputLengthArr, "content");
emit("finishCode", virfyLength);
}
/**
* @description: 焦点自动获取优化
* @param {*} event
* @param {*} index
* @return {*}
*/
function onChange(event, index) {
if (event.data) {
document.getElementById(nameId(index + 1))?.focus();
state.focusIndex = index + 1;
} else {
if (index === 0) return;
document.getElementById(nameId(index - 1))?.focus();
state.focusIndex = index - 1;
}
let virfyLength = _.sumBy(state.inputLengthArr, "content");
if (virfyLength.length === 6) {
submitGoogle();
}
}
function nameId(index) {
return "googel-input" + index;
}
</script>
依赖自定义工具函数-一键复制(原生)
关于复制到粘贴板