vue3在元素上绑定自定义事件弹出虚拟键盘

最近开发中遇到一个需求:

焊接机器人的屏幕上集成web前端网页, 但是没有接入键盘。这就需要web端开发一个虚拟键盘,在网上找个很多虚拟键盘没有特别适合,索性自己写个简单的

图片:

代码:

(代码可能比较垃圾冗余,也没时间优化,凑合看吧)

第一步:创建键盘组件

为了方便使用,我将键盘写成组件的方式,在app.vue中引入可以全局使用

javascript 复制代码
<template>
 <el-dialog
  v-model="isShows"
  append-to-body="true"
  width="80%"
  @close="dialogClose"
 >
  <div
   class="keyboard_pop"
   @click.self="isShows = false"
  >
   <div class="input">
    <span
     v-if="!showText"
     class="placeholder"
    >
     请输入内容</span
    >
    <p v-else>
     {{ showText }}
    </p>
   </div>

   <div class="keyboard">
    <div
     v-for="(row, index) in keyList"
     :key="index"
     class="keyRow"
    >
     <div
      v-for="(key, keyIndex) in row"
      :key="keyIndex"
      :class="{
       delete: key === 'Delete',
       capslock: key === 'Caps',
       space: key === 'Space',
       capsed: key === 'Caps' && hasCapsed,
       li: true,
      }"
      @click="clickKey(key)"
     >
      {{ key }}
     </div>
    </div>
   </div>

  </div>
 </el-dialog>
</template>
<script setup name="template">
 import useHomeStore from "@/stores/home"; //引入仓库
 import { storeToRefs } from "pinia"; //引入pinia转换
 import { ElMessage } from "element-plus";
 const userInfoStore = useHomeStore();
 const { isShows, showText, inputType } = storeToRefs(userInfoStore); // 响应式
 const emits = defineEmits(["updatekey"]);

 const keyvalue = ref(showText); //键盘输入值  this.keyboardtext

 const normalKeyList = ref([
  ["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", ";", "'", "Enter"],
  ["z", "x", "c", "v", "b", "n", "m", ",", ".", "/"],
  ["Caps", "Space", "Delete"],
 ]); //正常键盘列表

 const capsedKeyList = ref([
  ["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", ";", "'", "Enter"],
  ["Z", "X", "C", "V", "B", "N", "M", ",", ".", "/"],
  ["Caps", "Space", "Delete"],
 ]); //大写键盘列表
 const keyList = ref(normalKeyList.value); //键盘列表
 const hasCapsed = ref(false); //是否大写

 const clickKey = (key) => {
  if (inputType.value == "number") {
   const flag = [
    "0",
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
     "9",
    '.',
    "Enter",
    "Delete",
   ].includes(key);
   if (!flag) {
    return ElMessage({
     message: "请输入数字",
     type: "warning",
    });
   }
  }

  switch (key) {
    case "Enter":
    userInfoStore.showText = keyvalue.value;
    userInfoStore.clickEnter();
    break;
   case "Delete":
    let kbt = keyvalue.value;
    keyvalue.value = kbt.length ? kbt.substring(0, kbt.length - 1) : kbt;
    break;
   case "Space":
    keyvalue.value += " ";
    break;

   case "Caps":
    hasCapsed.value = !hasCapsed.value;
    keyList.value = hasCapsed.value ? capsedKeyList.value : normalKeyList.value;
    break;
   default:
    keyvalue.value += key;

    break;
  }
  userInfoStore.showText = keyvalue.value;

  const dialogClose = () => {
   //遮罩层关闭
   userInfoStore.dialogClose();
  };
 };
</script>
<style lang="scss" scoped>
 .input {
  min-height: 80px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin-bottom: 30px;
  padding: 10px;
 }
 .keyboard_pop {
  width: 100%;
 }
 .keyRow {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
 }
 .keyboard {
  margin: 0;
  padding: 0;
  list-style: none;
  user-select: none;
  background: #fff;
  width: 100%;

  padding: 5px 15px;
  overflow: auto;

  .li {
   padding: 8px 18px;
   text-align: center;
   background: #fff;
   border-radius: 15px;
   font-size: 18px;
   font-weight: 500;
   box-shadow: 0 3px 6px 0px #cac9c9;

   &:hover {
    cursor: pointer;
    background: #03ba82;
    color: #fff;
   }
   &:active {
    top: 1px;
    left: 1px;
   }
  }

  .delete {
   width: 100px;
  }
 
  .space {
   width: 300px;
  }
  .capsed {
   position: relative;
   top: 1px;
   left: 1px;
   border-color: #e5e5e5;
   cursor: pointer;
  }
 }
</style>

第二步:注册自定义事件

(给元素绑定自定义事件,input获取光标直接可以显示虚拟键盘,方便操作!)

javascript 复制代码
const isInput = (dom) => {
 // 检查dom是否是input元素
 if (dom.tagName === "INPUT") {
  return dom;
 }

 // 如果不是input元素,且有子节点,则递归查找子节点
 if (dom.children) {
  for (let child of dom.children) {
   let input = isInput(child);
   if (input) {
    return input; // 如果找到input元素,立即返回
   }
  }
 }
 // 如果没有找到input元素,返回null
 return null;
};

import pinia from "@/stores/index";
import useHomeStore from "@/stores/home"; //引入仓库
const userInfoStore = useHomeStore(pinia);

export const showKeyboard = {
 mounted(el, binding) {
  const input = isInput(el);
  if (!input) {
   return console.log("绑定错误,没有input元素");
  }
  //保存input元素
  userInfoStore.inputDom = input;
  //给input注册input事件
  input.addEventListener("focus", function (e) {
   console.log("聚焦了");
   console.dir(e);
   //保存input输入框的类型
   userInfoStore.inputType = e.target.type;
   userInfoStore.inputDom = e.target;
   userInfoStore.isShows = true;
   userInfoStore.showText = e.target.value;
   e.target.blur();
  });

  //   input.addEventListener("blur", function (e) {
  //    console.log("失去聚焦了");
  // //    userInfoStore.isShows = false;
  //   });
 },
};

第三步:pinia保存全局变量

(全局保存变量,各个input输入的值方便保存)

javascript 复制代码
import { defineStore } from "pinia";

const useHomeStore = defineStore("Home", {
 // defineStore('userInfo',{})  Home就是这个仓库的名称name
 state: () => ({
  inputDom: null,
  isShows: false,
  showText: "",
  inputType: "",
 }),
 actions: {
  //点击键盘确定
  clickEnter() {
   //this.inputDom.change()
   console.log(this.showText);
   this.inputDom.value = JSON.parse(JSON.stringify(this.showText))
   console.dir(this.inputDom);
   //手动触发change事件

   //  什么是dispatchEvent
   // 向一个指定的事件目标派发一个事件,
   //  并以合适的顺序同步调用目标元素相关的事件处理函数。
   //  标准事件处理规则(包括事件捕获和可选的冒泡过程)
   //  同样适用于通过手动的使用dispatchEvent()方法派发的事件。
   this.inputDom.dispatchEvent(new Event("input"));
   this.inputDom.dispatchEvent(new Event("change"));
   this.showText = "";
   this.inputDom.blur();
   this.isShows = false;
   this.inputDom = null;
   this.inputType = "";
  },
  //遮罩层关闭
  dialogClose() {
   this.showText = "";
   this.isShows = false;
   this.inputDom.blur();
   this.inputDom = null;
   this.inputType = "";
  },
 },
});

export default useHomeStore;
相关推荐
浮游本尊38 分钟前
关于考试监听切屏的三种方式
前端
GISer_Jing41 分钟前
2025年前端面试热门题目——HTML|CSS|Javascript|TS知识
前端·javascript·面试·html
m0_7482556542 分钟前
Springboot基于Web的景区疫情预警系统设计与实现5170q(程序+源码+数据库+调试部署+开发环境)
前端·数据库·spring boot
m0_7482370543 分钟前
web的五个Observer API
java·前端·javascript
小王爱吃月亮糖1 小时前
QT开发【常用控件1】-Layouts & Spacers
开发语言·前端·c++·qt·visual studio
feifeiyechuan1 小时前
【Chrome Extension】二、导航栏快速查询
前端·chrome·chromeextension
索然无味io1 小时前
跨站请求伪造之基本介绍
前端·笔记·学习·web安全·网络安全·php
m0_748256561 小时前
Windows 11 Web 项目常见问题解决方案
前端·windows
LOVE️YOU1 小时前
HTML&CSS&JavaScript&DOM 之间的关系?
前端·javascript·css·html
胡西风_foxww1 小时前
【es6复习笔记】集合Set(13)
前端·笔记·es6·set·集合