【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>
相关推荐
喵个咪8 分钟前
GoWind 拆解:全链 ID 类型体系——int64 定版与前端字符串化契约
前端·后端·go
喵个咪13 分钟前
GoWind CMS 拆解:Flutter 设计系统内核——双态 token、四档断点与自适应壳层
前端·flutter·交互设计
喵个咪36 分钟前
GoWind CMS 拆解:发布审批——多级推进、逐级角色与全程留痕
前端·后端·go
喵个咪1 小时前
GoWind Flow 拆解:一个 Go 工作流引擎与它的双模式设计器
前端·后端·go
喵个咪1 小时前
GoWind Admin 拆解:打印模板设计器——从画布到服务端 PDF 的完整打印链路
前端·后端·go
总要学点什么1 小时前
第96篇:三维数据加载-城市建筑白模样式+着色器——让色带按体量上色,最白的那档却涂不上去
前端·gis·webgl·cesium·着色器·空间分析·白模
Hilaku2 小时前
你们公司有 35 岁以上的前端吗?
前端·javascript·程序员
徐小夕2 小时前
开源Vector-Graph-RAG:向量+图谱双引擎,打破纯向量RAG的效果天花板
前端·vue.js·github
SameX2 小时前
HarmonyOS ArkTS 把组件截图保存成 PNG 再调系统分享面板的做法
前端
Swot2 小时前
SvelteKit SSG 发布后,为什么老访客非要强刷才能看到新内容?
前端