|
|
@ -1,5 +1,4 @@ |
|
|
|
<script lang="ts" setup> |
|
|
|
// import pinyinDict from 'libs/pinyinDict.json' |
|
|
|
import { computed, defineEmits, defineProps, onMounted, ref, watch, watchEffect } from 'vue' |
|
|
|
|
|
|
|
const props = defineProps<{ |
|
|
@ -14,9 +13,7 @@ const emits = defineEmits<{ |
|
|
|
(e: 'confirm', value: string): void |
|
|
|
(e: 'close'): void |
|
|
|
}>() |
|
|
|
const languageType = ref('en') |
|
|
|
// const cnList = ref<string[]>([]) |
|
|
|
// const pinyinMap: Record<string, string> = pinyinDict as unknown as Record<string, string> |
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
document.addEventListener('click', (e: any) => { |
|
|
|
if (isOpen.value && !e.target?.name) { |
|
|
@ -28,65 +25,52 @@ onMounted(() => { |
|
|
|
|
|
|
|
const isOpen = ref(false) |
|
|
|
watchEffect(() => { |
|
|
|
// 在焦点内二次点击时不触发EventListener,做一下延迟处理 |
|
|
|
setTimeout(() => { |
|
|
|
isOpen.value = props.isVisible |
|
|
|
}, 100) |
|
|
|
}) |
|
|
|
|
|
|
|
const activeKey = ref('') |
|
|
|
// 数字键盘布局 |
|
|
|
const numberLayout = computed(() => [ |
|
|
|
['1', '2', '3', 'del'], |
|
|
|
['4', '5', '6', '.'], |
|
|
|
['7', '8', '9', 'enter'], |
|
|
|
['+', '0', '-', 'enter'] |
|
|
|
]) |
|
|
|
// 字母键盘布局 |
|
|
|
const textLayout = computed(() => [ |
|
|
|
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'del'], |
|
|
|
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '/'], |
|
|
|
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 'enter'], |
|
|
|
['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '?', '.', ':'], |
|
|
|
[' ',] |
|
|
|
]) |
|
|
|
|
|
|
|
// 根据 keyboardType 动态切换布局 |
|
|
|
const keyboardLayout = computed(() => { |
|
|
|
if (props.keyboardType === 'number') { |
|
|
|
return [ |
|
|
|
['1', '2', '3'], |
|
|
|
['4', '5', '6'], |
|
|
|
['7', '8', '9'], |
|
|
|
['.', '0', 'del'], |
|
|
|
] |
|
|
|
} |
|
|
|
return [ |
|
|
|
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'del'], |
|
|
|
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '/'], |
|
|
|
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 'enter'], |
|
|
|
['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '?', '.', ':'], |
|
|
|
[' ', 'close'], |
|
|
|
] |
|
|
|
return props.keyboardType === 'number' ? numberLayout.value : textLayout.value |
|
|
|
}) |
|
|
|
const specialKeys = ['del', 'enter', ' '] |
|
|
|
// const handleKeyCn = (cn: string) => { |
|
|
|
// console.log('cn===', cn) |
|
|
|
// } |
|
|
|
|
|
|
|
const specialKeys = ['del', 'enter', ' ', '.', 'close'] |
|
|
|
|
|
|
|
const handleKeyPress = (key: string) => { |
|
|
|
activeKey.value = key |
|
|
|
setTimeout(() => { |
|
|
|
activeKey.value = '' |
|
|
|
}, 150) |
|
|
|
}, 10) |
|
|
|
|
|
|
|
if (key === 'del') { |
|
|
|
const value = props.modelValue.slice(0, -1) |
|
|
|
console.log('value--------', value) |
|
|
|
emits('update:modelValue', value) |
|
|
|
} |
|
|
|
else if (key === 'enter') { |
|
|
|
} else if (key === 'enter') { |
|
|
|
emits('confirm', props.modelValue) |
|
|
|
closeKeyboard() |
|
|
|
} |
|
|
|
else if (key === 'close') { |
|
|
|
} else if (key === 'close') { |
|
|
|
closeKeyboard() |
|
|
|
} |
|
|
|
else if ((key === 'en' || key === 'cn') && props.keyboardType === 'text') { |
|
|
|
languageType.value = key === 'en' ? 'cn' : 'en' |
|
|
|
keyboardLayout.value[4][1] = key === 'en' ? 'cn' : 'en' |
|
|
|
} |
|
|
|
else { |
|
|
|
} else { |
|
|
|
const keyValue = props.modelValue + key |
|
|
|
emits('update:modelValue', keyValue) |
|
|
|
// if (props.keyboardType === 'text' && languageType.value === 'cn') { |
|
|
|
// const cn = pinyinMap[keyValue] |
|
|
|
// if (cn.length) { |
|
|
|
// cnList.value = cn as unknown as string[] |
|
|
|
// } |
|
|
|
// } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -101,47 +85,58 @@ watch(() => props.isVisible, (newVal) => { |
|
|
|
</script> |
|
|
|
|
|
|
|
<template> |
|
|
|
<div v-if="isOpen" class="soft-keyboard" :class="{ 'keyboard-open': isOpen }"> |
|
|
|
<!-- <div class="keyboard-header"> |
|
|
|
<button @click="closeKeyboard"> |
|
|
|
关闭键盘 |
|
|
|
</button> |
|
|
|
</div> --> |
|
|
|
<div |
|
|
|
v-if="isOpen" |
|
|
|
class="soft-keyboard" |
|
|
|
:class="{ |
|
|
|
'keyboard-open': isOpen, |
|
|
|
'number-keyboard': keyboardType === 'number', |
|
|
|
'text-keyboard': keyboardType === 'text' |
|
|
|
}" |
|
|
|
> |
|
|
|
<div class="keyboard-body"> |
|
|
|
<!-- <div v-if="keyboardType === 'text' && cnList.length"> |
|
|
|
<button |
|
|
|
v-for="(cnName, cnIndex) in cnList" |
|
|
|
:key="cnIndex" |
|
|
|
@click="(e) => { |
|
|
|
e.stopPropagation() |
|
|
|
handleKeyCn(cnName) |
|
|
|
}" |
|
|
|
<el-input |
|
|
|
name="modelVal" |
|
|
|
v-model="props.modelValue" |
|
|
|
style="width: 93%; height: 5rem; font-size: 2rem; margin-bottom: 10px;" |
|
|
|
/> |
|
|
|
<div |
|
|
|
class="keyboard-grid" |
|
|
|
:class="{ |
|
|
|
'number-grid': keyboardType === 'number', |
|
|
|
'text-grid': keyboardType === 'text' |
|
|
|
}" |
|
|
|
> |
|
|
|
<div |
|
|
|
v-for="(row, rowIndex) in keyboardLayout" |
|
|
|
:key="rowIndex" |
|
|
|
class="keyboard-row" |
|
|
|
> |
|
|
|
{{ cnName }} |
|
|
|
</button> |
|
|
|
</div> --> |
|
|
|
<div> |
|
|
|
<el-input name="modelVal" v-model="props.modelValue" style="width: 93%;height: 5rem;font-size: 2rem;"></el-input> |
|
|
|
</div> |
|
|
|
<div class="keyboard-row" v-for="(row, index) in keyboardLayout" :key="index"> |
|
|
|
<button |
|
|
|
v-for="(key, keyIndex) in row" |
|
|
|
:key="keyIndex" |
|
|
|
style="font-size: 24px" |
|
|
|
:class="{ |
|
|
|
'key-space': key === ' ', |
|
|
|
'key-special': specialKeys.includes(key), |
|
|
|
'key-active': activeKey === key, |
|
|
|
'key-number': keyboardType === 'number', |
|
|
|
'key-text': key !== ' ' && keyboardType === 'text', |
|
|
|
}" |
|
|
|
@click="(e) => { |
|
|
|
e.stopPropagation() |
|
|
|
handleKeyPress(key) |
|
|
|
}" |
|
|
|
> |
|
|
|
{{ key === ' ' ? '空格' : key === 'del' ? '删除' : key === 'enter' ? '确认' : key === 'close' ? '关闭' : key }} |
|
|
|
</button> |
|
|
|
<div |
|
|
|
v-for="(key, colIndex) in row" |
|
|
|
:key="colIndex" |
|
|
|
class="keyboard-cell" |
|
|
|
:class="{ |
|
|
|
'key-active': activeKey === key, |
|
|
|
'key-special': specialKeys.includes(key)&& keyboardType === 'number', |
|
|
|
'key-confirm': key === 'enter', |
|
|
|
'key-space': key === ' ', |
|
|
|
'key-number-w': !specialKeys.includes(key) && keyboardType === 'number' |
|
|
|
}" |
|
|
|
@click="(e) => { |
|
|
|
e.stopPropagation() |
|
|
|
handleKeyPress(key) |
|
|
|
}" |
|
|
|
> |
|
|
|
{{ |
|
|
|
key === 'del' ? '删除' : |
|
|
|
key === 'enter' ? '确认' : |
|
|
|
key === 'close' ? '关闭' : |
|
|
|
key === ' ' ? '空格' : |
|
|
|
key |
|
|
|
}} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -165,70 +160,82 @@ watch(() => props.isVisible, (newVal) => { |
|
|
|
bottom: 0; |
|
|
|
} |
|
|
|
|
|
|
|
.keyboard-header { |
|
|
|
margin-bottom: 10px; |
|
|
|
position: absolute; |
|
|
|
float:right; |
|
|
|
.keyboard-body { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
align-items: center; |
|
|
|
padding: 10px; |
|
|
|
} |
|
|
|
|
|
|
|
.keyboard-header button { |
|
|
|
padding: 8px 15px; |
|
|
|
background-color: #e0e0e0; |
|
|
|
border: none; |
|
|
|
border-radius: 5px; |
|
|
|
cursor: pointer; |
|
|
|
/* 数字键盘网格布局 */ |
|
|
|
.number-grid { |
|
|
|
display: grid; |
|
|
|
grid-template-columns: repeat(4, 1fr); |
|
|
|
gap: 5px; |
|
|
|
width: 88vw; |
|
|
|
height: 30vh; |
|
|
|
} |
|
|
|
|
|
|
|
.keyboard-body { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
gap: 8px; |
|
|
|
/* 字母键盘网格布局 */ |
|
|
|
.text-grid { |
|
|
|
display: grid; |
|
|
|
grid-template-columns: repeat(11, 1fr); |
|
|
|
gap: 3px; |
|
|
|
width: 90vw; |
|
|
|
} |
|
|
|
|
|
|
|
.keyboard-row { |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
gap: 5px; |
|
|
|
display: contents; |
|
|
|
} |
|
|
|
|
|
|
|
.keyboard-row button { |
|
|
|
min-width: 50px; |
|
|
|
.keyboard-cell { |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
height: 5rem; |
|
|
|
font-size: 18px; |
|
|
|
border: none; |
|
|
|
border-radius: 5px; |
|
|
|
background-color: #fff; |
|
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
|
|
border: 1px solid #ddd; |
|
|
|
border-radius: 5px; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.2s; |
|
|
|
font-size: 2rem; |
|
|
|
} |
|
|
|
|
|
|
|
.keyboard-row button:hover { |
|
|
|
/* background-color: #e0e0e0; */ |
|
|
|
} |
|
|
|
|
|
|
|
.keyboard-row button:active { |
|
|
|
.keyboard-cell:active { |
|
|
|
transform: scale(0.95); |
|
|
|
} |
|
|
|
|
|
|
|
.key-space { |
|
|
|
width: 45vw;; |
|
|
|
.key-active { |
|
|
|
background-color: #a0c4ff; |
|
|
|
transform: scale(0.95); |
|
|
|
} |
|
|
|
|
|
|
|
.key-special { |
|
|
|
background-color: #e0e0e0; |
|
|
|
width: 9rem; |
|
|
|
height: 7rem; |
|
|
|
} |
|
|
|
|
|
|
|
.key-active { |
|
|
|
background-color: #a0c4ff; |
|
|
|
transform: scale(0.95); |
|
|
|
/* 数字键盘确认按钮跨行吗样式 */ |
|
|
|
.number-grid .key-confirm { |
|
|
|
grid-row: 3 / 5; |
|
|
|
grid-column: 4 / 5; |
|
|
|
height: auto; |
|
|
|
} |
|
|
|
|
|
|
|
.key-number{ |
|
|
|
width: 30vw; |
|
|
|
/* 字母键盘确认按钮样式 */ |
|
|
|
.text-grid .key-confirm { |
|
|
|
background-color: #e0e0e0; |
|
|
|
} |
|
|
|
|
|
|
|
/* 空格按钮样式(仅字母键盘) */ |
|
|
|
.key-space { |
|
|
|
grid-column: span 11; |
|
|
|
width: 90vw; |
|
|
|
} |
|
|
|
|
|
|
|
.key-text { |
|
|
|
width: 8vw; |
|
|
|
.key-number-w{ |
|
|
|
width: 15rem; |
|
|
|
height: 7rem; |
|
|
|
} |
|
|
|
</style> |
|
|
|
</style> |