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;
相关推荐
@大迁世界8 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路17 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug21 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213823 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中44 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端