【sgKeyboard】自定义组件:虚拟键盘

sgKeyboard

html 复制代码
<template>
  <div :class="$options.name">
    <div class="keyboard">
      <div class="key-row">
        <div class="key" v-for="(a, i) in rows[0]" :key="i" @mousedown="mousedown(a)">
          {{ a }}
        </div>
      </div>
      <div class="key-row" v-for="(a, i) in rows" :key="i">
        <div
          class="key"
          v-for="(key, subIndex) in rows[i + 1]"
          :key="subIndex"
          @mousedown="mousedown(getChar(key))"
        >
          {{ getChar(key) }}
        </div>
      </div>
      <div class="key-row">
        <div
          class="key key-wide capsLock"
          :capsLock="capsLock"
          :active="capsLock"
          @mousedown="toggleCaps"
        >
          切换为{{ capsLock ? `小` : `大` }}写
        </div>
        <div class="key key-space" @mousedown="mousedown(` `)">空格</div>
        <div class="key key-wide back-btn" @mousedown="handleBackspace">退格</div>
        <div class="key key-wide clear-btn" @mousedown="clearAll">清空</div>
      </div>
      <div class="close-btn" @click="$emit(`close`, true)" v-if="showCloseBtn">
        <i class="el-icon-close" />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: `sgKeyboard`,
  data() {
    return {
      keyword: ``,
      capsLock: false,
      rows: [
        [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
        [`a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`, `i`, `j`],
        [`k`, `l`, `m`, `n`, `o`, `p`, `q`, `r`, `s`, `t`],
        [`u`, `v`, `w`, `x`, `y`, `z`],
      ],
      showCloseBtn: false,
    };
  },
  props: [`value`, `data`],
  watch: {
    value: {
      handler(d) {
        this.keyword = d;
      },
      deep: true,
      immediate: true,
    },
    keyword(d) {
      this.$emit(`input`, d);
    },
    data: {
      handler(newValue, oldValue) {
        //console.log(`深度监听${this.$options.name}:`, newValue, oldValue);
        if (Object.keys(newValue || {}).length) {
          this.form = this.$g.deepCopy(newValue);
          this.$g.cF2CP(`disabled`, this);
          this.$g.cF2CP(`showCloseBtn`, this);
        }
      },
      deep: true,
      immediate: true,
    },
  },
  methods: {
    getChar(char) {
      return this.capsLock ? char.toUpperCase() : char;
    },
    mousedown(char) {
      this.keyword += char;
    },
    handleBackspace() {
      this.keyword.length > 0 &&
        (this.keyword = this.keyword.slice(0, this.keyword.length - 1));
    },
    toggleCaps() {
      this.capsLock = !this.capsLock;
    },
    clearAll() {
      this.keyword = ``;
    },
  },
};
</script>

<style lang="scss" scoped>
.sgKeyboard {
  .keyboard {
    position: relative;
    width: max-content;
    height: max-content;
    box-sizing: border-box;
    border: 1px solid #c6d1de;
    box-shadow: 0 5px 11px 0 #00000033;
    padding: 10px;
    background-color: #efefef;
    border-radius: 6px;
    color: #5e6d82;
    .key-row {
      display: flex;
      gap: 4px;
      margin-bottom: 4px;
    }
    .key {
      display: flex;
      justify-content: center;
      align-items: center;
      min-width: 36px;
      height: 36px;
      text-align: center;
      background-color: #fff;
      border: none;
      border-radius: 4px;
      font-size: 14px;
      font-family: Microsoft YaHei;
      box-shadow: 0 3px 5px 0 #00000044;
      cursor: pointer;
      box-sizing: border-box;
      border: 1px solid transparent;
      &:hover {
        background-color: #fafafa;
      }
      &:active,
      &[active] {
        transform: translate(0px, 1px);
        /*从上往下线性渐变背景*/
        background: linear-gradient(white, lightgray);
        box-shadow: 0 2px 3px 0 #00000022;
      }
      &[active] {
        border-color: #555;
        border-top: none;
      }
    }
    .key-wide {
      padding: 0 10px;
    }
    .capsLock {
      &[capsLock] {
        color: white;
        background: #7d7d7d;
      }
    }
    .key-space {
      flex-grow: 1;
    }
    .back-btn {
      width: 50px;
    }
    .clear-btn {
      width: 50px;
    }
    .close-btn {
      position: absolute;
      right: -10px;
      top: -10px;

      color: #c6d1de;
      background-color: white;
      box-sizing: border-box;
      border: 1px solid #c6d1de;
      box-shadow: 0 5px 10px 0 #00000033;
      border-radius: 88px;
      box-sizing: border-box;
      width: 20px;
      height: 20px;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
      i {
        font-size: 12px;
      }
      &:hover {
        color: white;
        background-color: #c6d1de;
      }
      &:active {
        transform: translate(1px, 1px);
        opacity: 0.618;
      }
    }
  }
}
</style>

demo

html 复制代码
<template>
  <div :class="$options.name">
    <el-input v-model="value" :placeholder="`输入内容`" clearable />
    <el-link
      type="primary"
      :underline="false"
      @click="show_sgKeyboard = !show_sgKeyboard"
      style="margin-top: 10px; margin-bottom: 10px"
      >{{ show_sgKeyboard ? `隐藏` : `使用` }}虚拟键盘</el-link
    >
    <sgKeyboard v-model="value" v-if="show_sgKeyboard" />
  </div>
</template>
<script>
import sgKeyboard from "@/vue/components/admin/sgKeyboard";
export default {
  name: `demo`,
  components: { sgKeyboard },
  data() {
    return { value: ``, show_sgKeyboard: true };
  },
};
</script>
<style lang="scss" scoped>
.demo {
  box-sizing: border-box;
  padding: 20px;
}
</style>
相关推荐
锅里游的鱼吖1 小时前
echarts自定义折线图
前端·javascript·数据库
hunteritself1 小时前
卷卷卷!GPT-6 Sol、Luna 正式发布,OpenAI 开始卷价格了
大数据·前端·人工智能·深度学习·transformer
何何____2 小时前
Vue 全局事件总线详解
前端·javascript·vue.js
涉密IT资质笔记3 小时前
涉密人员脱密期管理规范:期限分级模型、就业限制边界与违规认定标准
java·服务器·前端·网络·数据库
u0111026753 小时前
工具页案例图如何编写 alt让图片说明与处理场景对应
java·前端·javascript·图像处理·人工智能·算法·ai作画
事圆则缓3 小时前
Flutter 状态管理框架对比(五):GetX 的响应式、依赖注册与生命周期
前端·javascript·flutter
广州智造4 小时前
Radioss复合材料使用的单元属性
前端·设计·cad·建模·cae·radioss
ss2734 小时前
AI全栈实战 | 2.1-03 JavaScript 核心:单线程+事件循环为什么不等同于多线程,原型链 vs Java 继承
java·开发语言·javascript
张宏宇4 小时前
我把微软开源的 tgrep 做成了本地版 GitHub Code Search:45,629 个文件里搜一次 10ms
前端·后端