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;
相关推荐
我要洋人死39 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#