1、安装
simple-keyboard
2、keyborard封装组件
<SimpleKeyboard
:ref="keyboardClass"
:keyboardClass="keyboardClass"
@onChange="onChange"
@onKeyPress="onKeyPress"
:input="inputValue"
:maxLength="maxlength"
/>
组件代码
SimpleKeyboard.vue
Keyboard配置可以根据自己定义
<template>
<div :class="keyboardClass"></div>
</template>
<script lang="ts" setup>
import { reactive, ref, defineProps, watch, unref, onMounted, defineEmits } from 'vue'
import Keyboard from 'simple-keyboard'
import 'simple-keyboard/build/css/index.css'
import layout from 'simple-keyboard-layouts/build/layouts/chinese' // 中文输入法
const props = defineProps({
keyboardClass: {
default: 'simple-keyboard',
type: String
},
input: {
default: ''
},
maxLength: { default: '' }
})
const keyboard = ref(null)
const emit = defineEmits(['onChange', 'onKeyPress'])
const displayDefault = reactive({
'{bksp}': 'backspace',
'{lock}': 'caps',
'{enter}': '无',
'{tab}': 'tab',
'{shift}': 'shift',
'{change}': '中文',
'{space}': ' ',
'{clear}': '清空',
'{close}': '关闭'
})
watch(
() => unref(props).input,
(input) => {
keyboard.value.setInput(input)
}
)
function onChange(input) {
keyboard.value.setInput(input)
emit('onChange', input)
}
function handleShift() {
let currentLayout = keyboard.value.options.layoutName
let shiftToggle = currentLayout === 'default' ? 'shift' : 'default'
keyboard.value.setOptions({
layoutName: shiftToggle
})
}
function onKeyPress(button, $event) {
// 点击关闭
if (button === '{close}') {
let keyboard = $event.path[3]
keyboard.style.visibility = 'hidden'
return false
} else if (button === '{change}') {
// 切换中英文输入法
if (keyboard.value.options.layoutCandidates !== null) {
displayDefault['{change}'] = '中文'
// 切换至英文
keyboard.value.setOptions({
layoutCandidates: null,
display: displayDefault
})
} else {
// 切换至中文
displayDefault['{change}'] = '英文'
keyboard.value.setOptions({
layoutCandidates: layout.layoutCandidates,
display: displayDefault
})
}
} else if (button === '{clear}') {
keyboard.value.clearInput()
} else if (button === '{enter}') {
// keyboard.value.setInput('')
// emit('onKeyPress', button)
} else {
let value = $event.target.offsetParent.parentElement.children[0].children[0].value
// 输入框有默认值时,覆写
if (value) {
keyboard.value.setInput(value)
}
emit('onKeyPress', button)
}
if (button === '{shift}' || button === '{lock}') handleShift()
}
onMounted(() => {
keyboard.value = new Keyboard(props.keyboardClass, {
onChange: onChange,
onKeyPress: onKeyPress,
layoutCandidates: null,
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 < > ? {clear}',
'{change} {space} {close}'
]
},
// 按钮展示文字
display: displayDefault,
// 按钮样式
buttonTheme: [
{
class: 'hg-red close',
buttons: '{close}'
},
{
class: 'change',
buttons: '{change}'
}
],
// 输入限制长度
maxLength: props.maxLength
})
})
</script>
<style lang="less">
@deep: ~'>>>';
.hg-theme-default {
width: 70%;
.hg-button {
&.hg-red {
background: #db3e5d;
color: white;
&.close {
max-width: 200px;
}
}
&.change {
max-width: 200px;
}
}
}
</style>
3、ui库封装
KeyboardInput.js
<template>
<div class="input-keyboard">
<n-input
v-model:value="inputValue"
:autofocus="autofocus"
:class="inputClass"
:type="type"
:show-password-on="showPassword"
:rows="rows"
:disabled="disabled"
:maxlength="maxlength"
:clearable="clearable"
:showPasswordOn="showPasswordOn"
:size="size"
:placeholder="placeholder"
@focus="focusInput($event)"
@input="inputFun"
>
<slot></slot>
</n-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: 'KeyboardInput',
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,
disabled: {
type: Boolean,
default: false
},
maxlength: Number,
clearable: {
type: Boolean,
default: true
},
showPasswordOn: {
type: String,
default: ''
},
size: String,
placeholder: String,
autocomplete: {
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) {
this.$emit('inputChange', button)
}
}
}
</script>
<style lang="less" scoped>
@deep: ~'>>>';
.input-keyboard {
width: 100%;
@{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 {
width: 100% !important;
}
}
// 密码输入框显示密码图标效果
@{deep}.el-input__inner[type='text'] + span .el-icon-view {
color: red;
}
}
</style>
4、最外层调用
<keyboard-input
keyboardClass="login-password"
field="password"
type="password"
:value="formInline.password"
:maxlength="30"
showPasswordOn="click"
@inputChange="formItemChange"
placeholder="请输入密码"
>
</keyboard-input>
import KeyboardInput from '@/components/SimpleKeyboard/KeyboardInput.vue'
5、最后在使用input外层div,加入监听点击关闭键盘
@click="judgeCloseKeyboard"
import { judgeCloseKeyboard } from '@/utils'
/**
* 点击除输入框及虚拟键盘外元素时,隐藏虚拟键盘
* @param {e} 点击的元素
*/
export const judgeCloseKeyboard = (e) => {
e.stopPropagation() // 阻止事件冒泡
const arr = document.querySelectorAll('.input-keyboard') // 设置目标区域
if (!arr || (arr && arr.length === 0)) return
let flag = false
const excludeClassName = [
'n-input__input', // 带虚拟键盘的输入框
'n-input__input-el', // 输入框
'n-base-clear', // 输入框-清除图标
'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' // 虚拟键盘-按钮
]
const classList = e.target.classList.value.split(' ')
const 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 = () => {
console.log('我出发了')
const arr = document.querySelectorAll('.hg-theme-default')
arr.forEach((ele) => {
ele.style.visibility = 'hidden'
})
}
excludeClassName里面是点击不会关闭键盘的classname