触摸屏-直流
parent
1986634e17
commit
3d5d523da7
@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<div ref="keyboardWrapRef" class="keyboard-wrap" @click="focusInput">
|
||||
<el-input ref="inputRef" v-model="model" @keyup.enter="handleEnter" :disabled="disabled" v-bind="$attrs">
|
||||
<!-- <template v-for="(item, index) in $slots" :key="index" #[index]>
|
||||
<slot :name="index"></slot>
|
||||
</template> -->
|
||||
<template #suffix>
|
||||
<span>%</span>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-popover v-if="visible" :visible="true" :virtual-ref="inputRef" virtual-triggering placement="bottom" :width="width" :show-arrow="false" :hide-after="0" popper-style="padding: 0px;color:#000" popper-class="keyboard-popper" @after-enter="keyboardInit">
|
||||
<div class="simple-keyboard"></div>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Keyboard from 'simple-keyboard'
|
||||
import 'simple-keyboard/build/css/index.css'
|
||||
import layout from 'simple-keyboard-layouts/build/layouts/chinese.js'
|
||||
defineOptions({
|
||||
inheritAttrs: false
|
||||
})
|
||||
const model = defineModel<any>()
|
||||
const emits = defineEmits(['onChange', 'enter', 'close', 'focus'])
|
||||
const props = defineProps({
|
||||
layoutName: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
// 保留几位小数 layoutName为number时生效
|
||||
precision: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
// 获取焦点打开键盘
|
||||
isOpen: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disabled: Boolean
|
||||
})
|
||||
|
||||
const keyboard = ref<any>(null)
|
||||
const keyboardWrapRef = ref(null)
|
||||
const visible = ref(false)
|
||||
const inputRef = ref()
|
||||
const width = ref(1000)
|
||||
if (props.layoutName == 'number') width.value = 300
|
||||
|
||||
const displayDefault = ref({
|
||||
'{bksp}': 'backspace',
|
||||
'{lock}': 'caps',
|
||||
'{enter}': 'enter',
|
||||
'{tab}': 'tab',
|
||||
'{shift}': 'shift',
|
||||
'{change}': 'en',
|
||||
'{space}': 'space',
|
||||
'{clear}': 'clear',
|
||||
'{close}': 'close',
|
||||
'{arrowleft}': '←',
|
||||
'{arrowright}': '→'
|
||||
})
|
||||
|
||||
const open = () => {
|
||||
if (visible.value) return
|
||||
inputRef.value.focus()
|
||||
emits('focus')
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
const focusInput = () => {
|
||||
if (props.disabled) return
|
||||
if (visible.value) return
|
||||
emits('focus')
|
||||
if (props.isOpen) visible.value = true
|
||||
}
|
||||
|
||||
const lang: any = {
|
||||
'zh-cn': {
|
||||
// 默认布局
|
||||
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}",
|
||||
'{change} z x c v b n m , . / {clear}',
|
||||
'{arrowleft} {arrowright} {space} {close}'
|
||||
],
|
||||
// 大小写
|
||||
shift: [
|
||||
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
|
||||
'{tab} Q W E R T Y U I O P { } |',
|
||||
'{lock} A S D F G H J K L : " {enter}',
|
||||
'{change} Z X C V B N M < > ? {clear}',
|
||||
'{arrowleft} {arrowright} {space} {close}'
|
||||
]
|
||||
},
|
||||
th: {
|
||||
default: [
|
||||
'\u005F \u0E45 \u002F \u002D \u0E20 \u0E16 \u0E38 \u0E36 \u0E04 \u0E15 \u0E08 \u0E02 \u0E0A {bksp}',
|
||||
'{tab} \u0E46 \u0E44 \u0E33 \u0E1E \u0E30 \u0E31 \u0E35 \u0E23 \u0E19 \u0E22 \u0E1A \u0E25 \u0E03',
|
||||
'{lock} \u0E1F \u0E2B \u0E01 \u0E14 \u0E40 \u0E49 \u0E48 \u0E32 \u0E2A \u0E27 \u0E07 {enter}',
|
||||
'{shift} \u0E1C \u0E1B \u0E41 \u0E2D \u0E34 \u0E37 \u0E17 \u0E21 \u0E43 \u0E1D {clear}',
|
||||
'{arrowleft} {arrowright} {space} {close}'
|
||||
],
|
||||
shift: [
|
||||
'% + \u0E51 \u0E52 \u0E53 \u0E54 \u0E39 \u0E3F \u0E55 \u0E56 \u0E57 \u0E58 \u0E59 {bksp}',
|
||||
'{tab} \u0E50 \u0022 \u0E0E \u0E11 \u0E18 \u0E4D \u0E4A \u0E13 \u0E2F \u0E0D \u0E10 \u002C \u0E05',
|
||||
'{lock} \u0E24 \u0E06 \u0E0F \u0E42 \u0E0C \u0E47 \u0E4B \u0E29 \u0E28 \u0E0B \u002E {enter}',
|
||||
'{shift} ( ) \u0E09 \u0E2E \u0E3A \u0E4C \u003F \u0E12 \u0E2C \u0E26 {clear}',
|
||||
'{arrowleft} {arrowright} {space} {close}'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const keyboardInit = () => {
|
||||
let keyboardLayout = {
|
||||
// 数字布局
|
||||
number: ['7 8 9', '4 5 6', '1 2 3', '. 0 {bksp}', '{arrowleft} {arrowright} {clear} {close}'],
|
||||
...lang['zh-cn']
|
||||
}
|
||||
|
||||
// if (lang[useSession('lang').value]) {
|
||||
// keyboardLayout = { ...lang[useSession('lang').value], ...keyboardLayout }
|
||||
// }
|
||||
keyboard.value = new Keyboard('simple-keyboard', {
|
||||
onChange: onChange,
|
||||
onKeyPress: onKeyPress,
|
||||
onInit: onInit,
|
||||
layout: keyboardLayout,
|
||||
layoutName: props.layoutName,
|
||||
display: displayDefault.value
|
||||
// theme: 'hg-theme-default init-keyboard' // 添加自定义class处理清空逻辑
|
||||
})
|
||||
}
|
||||
|
||||
const onInit = (keyboard: any) => {
|
||||
keyboard.setInput(String(model.value || ''))
|
||||
keyboard.setCaretPosition(inputRef.value?.ref.selectionEnd)
|
||||
document.addEventListener('click', handlePopClose)
|
||||
}
|
||||
const onChange = (input: any) => {
|
||||
model.value = input
|
||||
emits('onChange', input)
|
||||
}
|
||||
|
||||
const onKeyPress = (button: any) => {
|
||||
if (button === '{lock}') return handleLock()
|
||||
if (button === '{change}') return handleChange()
|
||||
if (button === '{clear}') return handleClear()
|
||||
if (button === '{enter}') return handleEnter()
|
||||
if (button === '{close}') return close()
|
||||
if (button === '{arrowleft}') return handleArrow(0)
|
||||
if (button === '{arrowright}') return handleArrow(1)
|
||||
}
|
||||
const handleLock = () => {
|
||||
let currentLayout = keyboard.value.options.layoutName
|
||||
let shiftToggle = currentLayout === 'default' ? 'shift' : 'default'
|
||||
keyboard.value.setOptions({
|
||||
layoutName: shiftToggle
|
||||
})
|
||||
}
|
||||
const handleChange = () => {
|
||||
let layoutCandidates = keyboard.value.options.layoutCandidates
|
||||
// 切换中英文输入法
|
||||
if (layoutCandidates != null && layoutCandidates != undefined) {
|
||||
displayDefault.value['{change}'] = 'en'
|
||||
keyboard.value.setOptions({
|
||||
layoutName: 'default',
|
||||
layoutCandidates: null,
|
||||
display: displayDefault.value
|
||||
})
|
||||
} else {
|
||||
displayDefault.value['{change}'] = 'cn'
|
||||
keyboard.value.setOptions({
|
||||
layoutName: 'default',
|
||||
layoutCandidates: (layout as any)?.layoutCandidates,
|
||||
display: displayDefault.value
|
||||
})
|
||||
}
|
||||
}
|
||||
const handleClear = () => {
|
||||
keyboard.value.clearInput()
|
||||
model.value = ''
|
||||
}
|
||||
const handleEnter = () => {
|
||||
emits('enter')
|
||||
}
|
||||
const close = () => {
|
||||
if (props.layoutName == 'number') {
|
||||
// 处理精度
|
||||
// model.value = model.value?.replace(new RegExp(`(\\d+)\\.(\\d{${String(props.precision)}}).*$`), '$1.$2').replace(/\.$/, '')
|
||||
model.value = Number(model.value)
|
||||
}
|
||||
document.removeEventListener('click', handlePopClose)
|
||||
visible.value = false
|
||||
emits('close')
|
||||
}
|
||||
const handleArrow = (num: number) => {
|
||||
// 处理左右箭头下标位置
|
||||
const index = keyboard.value.getCaretPositionEnd()
|
||||
if (num == 0 && index - 1 >= 0) {
|
||||
keyboard.value.setCaretPosition(index - 1)
|
||||
} else if (num == 1 && index + 1 <= (model.value?.length || 0)) {
|
||||
keyboard.value.setCaretPosition(index + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const handlePopClose = (e: any) => {
|
||||
// 虚拟键盘区域 输入框区域 中文选项区域
|
||||
if (
|
||||
(e.target as Element).closest('.keyboard-popper') ||
|
||||
(e.target as Element).closest('.keyboard-wrap') == keyboardWrapRef.value ||
|
||||
/hg-candidate-box/.test(e.target.className)
|
||||
) {
|
||||
const index = keyboard.value.getCaretPositionEnd()
|
||||
inputRef.value.ref.selectionStart = inputRef.value.ref.selectionEnd = index
|
||||
inputRef.value.focus()
|
||||
} else {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
// 某些情况下未触发动画关闭时销毁事件。此处销毁做后备处理
|
||||
document.removeEventListener('click', handlePopClose)
|
||||
})
|
||||
|
||||
defineExpose({ inputRef, visible, open, close, keyboardInit })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.keyboard-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.hg-theme-default .hg-button.hg-button-arrowleft,
|
||||
.hg-theme-default .hg-button.hg-button-arrowright {
|
||||
max-width: 70px;
|
||||
}
|
||||
|
||||
.hg-theme-default .hg-button.hg-button-close {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.hg-layout-number .hg-button.hg-button-close {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.hg-layout-number .hg-button.hg-button-bksp {
|
||||
max-width: 92px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in new issue