vue 软键盘组件封装
场景和需求
1 软键盘固定
2 多输入框共用一个组件,聚焦切换时操作对象自动切换
3 根据光标在输入框的位置进行相应的输入和删除操作
4 点击软键盘时保存输入框光标活跃
5 输入框和键盘在一个弹窗组件中,弹窗打开时对其中一个输入框默认聚焦
6 样式方面,按键宽度自适应,可以给特定按键特定样式。
7 场景下特殊需求,当不对任意输入框聚焦时,默认对其中某个输入框进行操作。
实现
光标这个没做到输入框保持活跃,和删除光标处内容。
这里是获取光标位置的函数,但是当点击按钮时输入框就会blur,就无法获取了。
//获取光标位置函数
getCursorPosition(id) {
const elem = document.querySelector(id);
var pp = 0;
// IE
if (document.selection) {
elem.focus();
var aa = document.selection.createRange();
aa.moveStart('character', -elem.value.length);
pp = aa.text.length;
}
// FF, Chrome
else if (elem.selectionStart || elem.selectionStart == '0') {
pp = elem.selectionStart;
}
return pp;
},
子组件
<template>
<div class="keyboards">
<div class="keyboards-row" v-for="(keyArr, index) in activeKey" :key="index">
<button
class="keyboards-key"
v-for="item in keyArr"
v-text="item"
:key="item + index"
:class="{
'keyboards-key--active': item === activeButton,
'keyboards-key--number': type === 'number',
'keyboards-key--all': type !== 'number',
'keyboards-key--star': item === '*',
'keyboards-key--option': isOption(type, item)
}"
@click="clickKey(item)"
@mousedown="changeActiveButton(item)"
/>
</div>
</div>
</template>
<script>
export default {
name: 'SoftKeyboard',
props: {
type: {
type: String,
// number || uppercase || lowercase
default: () => 'number'
},
inputText: {
type: String,
default: () => ''
},
inputDom: {
type: String,
default: () => ''
}
},
data() {
return {
activeKey: [],
isUppercase: '',
activeButton: '', // 当前按压按钮
// 数字键盘布局
numberKey: [
['7', '8', '9'],
['4', '5', '6'],
['1', '2', '3'],
['0', '*', '删除', '清空']
],
// 全键布局
allBigKey: [
['!', '@', '#', '$', '%', '^', '&', '*', '-', '_'],
['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
['切换', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],
['删除', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '.', '清空']
],
allsmallKey: [
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
['切换', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'],
['删除', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '.', '清空']
]
};
},
created() {
this.changeKeyborad(this.type);
if (this.type === 'uppercase') {
this.isUppercase = true;
}
document.addEventListener('mouseup', this.mouseUp);
},
mounted() {},
destroyed() {
// 组件销毁时移除添加到document上的事件,避免影响到其他组件
document.removeEventListener('mouseup', this.mouseUp);
},
methods: {
changeKeyborad(type) {
switch (type) {
case 'number':
this.activeKey = this.numberKey;
break;
case 'uppercase':
this.activeKey = this.allBigKey;
break;
case 'lowercase':
this.activeKey = this.allsmallKey;
break;
default:
break;
}
},
isOption(type, btn) {
if (type === 'number') {
return false;
}
if (btn === '切换' || btn === '删除' || btn === '清空') {
return true;
}
return false;
},
clickKey(key) {
let value = this.inputText;
switch (key) {
case '删除':
value = value.length ? value.slice(0, -1) : value;
break;
case '清空':
value = '';
break;
case '切换':
this.isUppercase = !this.isUppercase;
this.activeKey = this.isUppercase ? this.allBigKey : this.allsmallKey;
break;
default:
value = value + key;
break;
}
this.$emit('onChange', value);
},
changeActiveButton(button) {
this.activeButton = button;
},
//获取光标位置函数
getCursorPosition(id) {
const elem = document.querySelector(id);
var pp = 0;
// IE
if (document.selection) {
elem.focus();
var aa = document.selection.createRange();
aa.moveStart('character', -elem.value.length);
pp = aa.text.length;
}
// FF, Chrome
else if (elem.selectionStart || elem.selectionStart == '0') {
pp = elem.selectionStart;
}
return pp;
},
mouseUp() {
this.activeButton = '';
}
}
};
</script>
<style scoped lang="scss">
.keyboards {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
background-color: #f0f0f0;
padding: 3px;
border-radius: 5px;
.keyboards-row {
width: 100%;
display: flex;
justify-content: space-around;
.keyboards-key {
width: 100%;
height: 56px;
margin: 2px;
padding: 5px;
flex-grow: 0;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #dedede;
// border-radius: 4px;
border-bottom: 1px solid #b5b5b5;
border-radius: 5px;
-webkit-box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.3);
box-shadow: 0 0 3px -1px rgba(0, 0, 0, 0.3);
cursor: pointer;
font-size: 1.5em;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Noto Sans CJK SC,
WenQuanYi Micro Hei, Arial, sans-serif;
font-weight: 400;
background-color: #fdfdfd;
// &:hover {
// box-shadow: 0 0 4px 2px #ccd1e7;
// }
&.keyboards-key--active {
background-color: #e5e5e5;
}
// &.keyboards-key--number {
// width: 32%;
// }
&.keyboards-key--all {
width: 9.1%;
}
&.keyboards-key--star {
font-size: 2em;
}
&.keyboards-key--option {
width: 20%;
}
}
}
}
</style>
父组件
<el-form ref="modelForm" :model="modelForm" :rules="modelRules" label-width="60px" size="medium">
<el-form-item label="输入框1" prop="input1">
<el-input
id="input-input1"
size="small"
clearable
v-model.trim="modelForm.input1"
@focus="setInput('input1')"
style="width: 100%"
></el-input>
</el-form-item>
<el-form-item label="输入框2" prop="input2">
<el-input
id="input-input2"
size="small"
maxlength="3"
ref="input2"
clearable
v-model.trim="modelForm.input2"
@focus="setInput('input2')"
></el-input>
</el-form-item>
</el-form>
<softKeyBoard @onChange="onChange" type="number" :inputText="input" :inputDom="inputDom" />
<script>
export default {
computed: {
input() {
if (this.activeInput) {
return this.modelForm[this.activeInput];
}
return '';
}
},
data() {
return {
activeInput: '',
};
},
methods: {
setInput(prop) {
this.activeInput = prop;
},
onChange(input) {
if (this.activeInput === 'input1') {
input = input.slice(0, 7);
}
if (this.activeInput === 'input2') {
input = input.slice(0, 3);
}
this.$set(this.modelForm, this.activeInput, input);
},
}
}
</script>