消毒机设备
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

294 lines
7.3 KiB

<script setup lang="ts">
import 'simple-keyboard/build/css/index.css'
import Keyboard from 'simple-keyboard'
import layout from 'simple-keyboard-layouts/build/layouts/chinese.js'
import { useDeviceStore } from 'stores/deviceStore'
import { computed, onUnmounted, ref } from 'vue'
defineOptions({
inheritAttrs: false,
})
const props = defineProps({
layoutName: {
type: String,
default: 'default',
},
// 保留几位小数 layoutName为number时生效
precision: {
type: Number,
default: 2,
},
})
const emits = defineEmits(['onChange', 'enter', 'close', 'focus'])
const deviceStore = useDeviceStore()
const deviceType = computed(() => {
return __DEVICE_TYPE__
})
const model = defineModel()
const keyboard = ref<any>(null)
const visible = ref(false)
const inputRef = ref()
const popoverRef = ref()
const entering = ref(false)
const width = ref(800)
if (props.layoutName === 'number')
width.value = 300
const displayDefault = ref({
'{bksp}': 'backspace',
'{lock}': 'caps',
'{enter}': '回车',
'{tab}': 'tab',
'{shift}': 'shift',
'{change}': 'en',
'{space}': 'space',
'{clear}': '清空',
'{close}': '关闭',
'{arrowleft}': '←',
'{arrowright}': '→',
})
const open = () => {
if (visible.value)
return
inputRef.value.focus()
emits('focus')
visible.value = true
}
const focusInput = (e: any) => {
const rect = e.target.getBoundingClientRect()
if (rect.top + window.scrollY > 800) {
placement.value = 'top'
}
else {
placement.value = 'bottom'
}
if (visible.value)
return
if (deviceType.value !== deviceStore.deviceTypeMap.LargeSpaceDM_B)
visible.value = true
}
emits('focus')
// const blurInput = debounce(() => {
// if (!entering.value) {
// handleClose()
// } else {
// entering.value = false
// }
// }, 100)
const blurInput = () => {
setTimeout(() => {
if (!entering.value) {
handleClose()
}
else {
entering.value = false
}
}, 100)
}
const afterEnter = () => {
// 存在上一个实例时移除元素
const prevKeyboard = document.querySelectorAll('.init-keyboard')
if (prevKeyboard.length > 0)
prevKeyboard[0]?.remove()
keyboard.value = new Keyboard('simple-keyboard', {
onChange,
onKeyPress,
onInit,
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}',
'{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}',
],
// 数字布局
number: ['7 8 9', '4 5 6', '1 2 3', '. 0 {bksp}', '{arrowleft} {arrowright} {clear} {close}'],
},
layoutName: props.layoutName,
display: displayDefault.value,
theme: 'hg-theme-default init-keyboard', // 添加自定义class处理清空逻辑
})
}
const beforeLeave = () => {
visible.value = false
entering.value = false
inputRef.value.blur()
displayDefault.value['{change}'] = 'en'
document.removeEventListener('click', handlePopClose)
}
const onInit = (keyboard: any) => {
keyboard.setInput(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 handleClose()
if (button === '{arrowleft}')
return handleArrow(0)
if (button === '{arrowright}')
return handleArrow(1)
}
const handleLock = () => {
entering.value = true
const currentLayout = keyboard.value.options.layoutName
const shiftToggle = currentLayout === 'default' ? 'shift' : 'default'
keyboard.value.setOptions({
layoutName: shiftToggle,
})
}
const handleChange = () => {
entering.value = true
const 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}'] = '中'
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 handleClose = () => {
if (props.layoutName === 'number') {
// 处理精度
// model.value = model.value?.replace(new RegExp(`(\\d+)\\.(\\d{${props.precision}}).*$`), '$1.$2').replace(/\.$/, '')
}
popoverRef.value.hide()
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 === inputRef.value?.ref
|| /hg-candidate-box/.test(e.target.className)
) {
entering.value = true
const index = keyboard.value.getCaretPositionEnd()
inputRef.value.ref.selectionStart = inputRef.value.ref.selectionEnd = index
inputRef.value.focus()
}
}
const close = () => {
handleClose()
}
const placement = ref('bottom')
onUnmounted(() => {
// 某些情况下未触发动画关闭时销毁事件。此处销毁做后备处理
document.removeEventListener('click', handlePopClose)
})
defineExpose({ inputRef, visible, open, close })
</script>
<template>
<el-input
ref="inputRef"
v-model="model"
v-bind="$attrs"
@focus="focusInput"
@blur="blurInput"
@keyup.enter="handleEnter"
>
<template v-for="(item, index) in $slots" :key="index" #[index]>
<slot :name="index" />
</template>
</el-input>
<el-popover
ref="popoverRef"
:visible="visible"
:virtual-ref="inputRef"
virtual-triggering
:width="width"
:show-arrow="false"
:hide-after="0"
:placement="placement"
popper-style="padding: 0px;color:#000"
:persistent="false"
popper-class="keyboard-popper"
@after-enter="afterEnter"
@before-leave="beforeLeave"
>
<div class="simple-keyboard" />
</el-popover>
</template>
<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>