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.
150 lines
3.4 KiB
150 lines
3.4 KiB
<script lang="ts" setup>
|
|
import type { Ref } from 'vue'
|
|
import { onMounted, ref } from 'vue'
|
|
|
|
// 定义组件状态
|
|
const inputValue = ref('')// 输入框内容
|
|
const keyboardRef = ref() as Ref<HTMLDivElement> // 软键盘容器 DOM
|
|
|
|
// 拖动相关状态
|
|
const isDragging = ref(false)// 是否正在拖动
|
|
const startX = ref(0)// 触摸起始 X
|
|
const startY = ref(0)// 触摸起始 Y
|
|
const x = ref(0)// 容器偏移 X
|
|
const y = ref(0)// 容器偏移 Y
|
|
|
|
// 数字键盘按键配置
|
|
const keyboardKeys = ref(['1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '0', '删除'])
|
|
|
|
// 触摸开始:记录初始位置
|
|
const handleTouchStart = (e: TouchEvent) => {
|
|
isDragging.value = true
|
|
const touch = e.touches[0]
|
|
startX.value = touch.clientX - x.value
|
|
startY.value = touch.clientY - y.value
|
|
}
|
|
|
|
// 触摸移动:计算偏移量
|
|
const handleTouchMove = (e: TouchEvent) => {
|
|
if (isDragging.value) {
|
|
const touch = e.touches[0]
|
|
x.value = touch.clientX - startX.value
|
|
y.value = touch.clientY - startY.value
|
|
}
|
|
}
|
|
|
|
// 触摸结束:停止拖动
|
|
const handleTouchEnd = () => {
|
|
isDragging.value = false
|
|
}
|
|
|
|
// 按键点击事件
|
|
const handleKeyClick = (key: string) => {
|
|
if (key === '删除') {
|
|
// 删除逻辑:移除最后一位
|
|
inputValue.value = inputValue.value.slice(0, -1)
|
|
}
|
|
else {
|
|
// 数字/小数点输入逻辑
|
|
inputValue.value += key
|
|
}
|
|
}
|
|
|
|
// 初始化:让软键盘居中(可选)
|
|
onMounted(() => {
|
|
const { width, height } = keyboardRef.value.getBoundingClientRect()
|
|
x.value = (document.documentElement.clientWidth - width) / 2
|
|
y.value = (document.documentElement.clientHeight - height) / 2
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<!-- 软键盘容器:支持拖动、点击穿透(若需) -->
|
|
<div
|
|
ref="keyboardRef"
|
|
class="keyboard-container"
|
|
@touchstart="handleTouchStart"
|
|
@touchmove="handleTouchMove"
|
|
@touchend="handleTouchEnd"
|
|
:style="{
|
|
transform: `translate(${x}px, ${y}px)`,
|
|
transition: isDragging ? 'none' : 'transform 0.3s ease',
|
|
}"
|
|
>
|
|
<!-- 输入显示框 -->
|
|
<div class="input-box">
|
|
<input
|
|
v-model="inputValue"
|
|
type="text"
|
|
placeholder="点击输入"
|
|
class="input-field"
|
|
@click.stop
|
|
/>
|
|
</div>
|
|
|
|
<!-- 数字按键区域 -->
|
|
<div class="keys">
|
|
<div
|
|
v-for="(key, index) in keyboardKeys"
|
|
:key="index"
|
|
class="key"
|
|
@click="handleKeyClick(key)"
|
|
>
|
|
{{ key }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.keyboard-container {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
max-width: 500px; /* 限制最大宽度 */
|
|
background: #fff;
|
|
border-radius: 16px 16px 0 0;
|
|
box-shadow: 0 -4px 12px rgba(0,0,0,0.1);
|
|
overflow: hidden;
|
|
z-index: 9999;
|
|
/* 让拖动更顺滑 */
|
|
will-change: transform;
|
|
touch-action: none; /* 禁止浏览器默认触摸行为(如滚动) */
|
|
}
|
|
|
|
.input-box {
|
|
padding: 16px;
|
|
background: #f5f5f5;
|
|
}
|
|
|
|
.input-field {
|
|
width: 100%;
|
|
padding: 12px;
|
|
text-align: right;
|
|
font-size: 18px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
background: #fff;
|
|
}
|
|
|
|
.keys {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
}
|
|
|
|
.key {
|
|
height: 60px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
font-size: 20px;
|
|
border: 1px solid #eee;
|
|
background: #fff;
|
|
touch-action: manipulation; /* 加速点击响应 */
|
|
}
|
|
|
|
.key:active {
|
|
background: #f8f8f8;
|
|
}
|
|
</style>
|